Files
2025-05-12 15:45:02 +08:00

214 lines
6.4 KiB
PHP

<?php
namespace Overtrue\Socialite\Providers;
use JetBrains\PhpStorm\Pure;
use Overtrue\Socialite\Contracts;
use Overtrue\Socialite\Exceptions;
use Overtrue\Socialite\User;
/**
* @link https://developer.work.weixin.qq.com/document/path/91022
*/
class WeWork extends Base
{
public const NAME = 'wework';
protected bool $detailed = false;
protected ?int $agentId = null;
protected ?string $apiAccessToken;
protected bool $asQrcode = false;
protected string $baseUrl = 'https://qyapi.weixin.qq.com';
public function __construct(array $config)
{
parent::__construct($config);
if ($this->getConfig()->has('base_url')) {
$this->baseUrl = $this->getConfig()->get('base_url');
}
if ($this->getConfig()->has('agent_id')) {
$this->agentId = $this->getConfig()->get('agent_id');
}
}
public function getBaseUrl(): ?string
{
return $this->baseUrl;
}
public function userFromCode(string $code): Contracts\UserInterface
{
$token = $this->getApiAccessToken();
$user = $this->getUser($token, $code);
if ($this->detailed) {
$user = $this->getUserById($user['UserId']);
}
return $this->mapUserToObject($user)->setProvider($this)->setRaw($user);
}
public function withAgentId(int $agentId): self
{
$this->agentId = $agentId;
return $this;
}
public function detailed(): self
{
$this->detailed = true;
return $this;
}
public function asQrcode(): self
{
$this->asQrcode = true;
return $this;
}
public function withApiAccessToken(string $apiAccessToken): self
{
$this->apiAccessToken = $apiAccessToken;
return $this;
}
public function getAuthUrl(): string
{
$scopes = $this->formatScopes($this->scopes, $this->scopeSeparator);
$queries = array_filter([
'appid' => $this->getClientId(),
'agentid' => $this->agentId,
Contracts\RFC6749_ABNF_REDIRECT_URI => $this->redirectUrl,
Contracts\RFC6749_ABNF_RESPONSE_TYPE => Contracts\RFC6749_ABNF_CODE,
Contracts\RFC6749_ABNF_SCOPE => $scopes,
Contracts\RFC6749_ABNF_STATE => $this->state,
]);
if (! $this->agentId && (str_contains($scopes, 'snsapi_privateinfo') || $this->asQrcode)) {
throw new Exceptions\InvalidArgumentException("agent_id is require when qrcode mode or scopes is 'snsapi_privateinfo'");
}
if ($this->asQrcode) {
unset($queries[Contracts\RFC6749_ABNF_SCOPE]);
return \sprintf('https://open.work.weixin.qq.com/wwopen/sso/qrConnect?%s', http_build_query($queries));
}
return \sprintf('https://open.weixin.qq.com/connect/oauth2/authorize?%s#wechat_redirect', \http_build_query($queries));
}
/**
* @throws Exceptions\MethodDoesNotSupportException
*/
protected function getUserByToken(string $token): array
{
throw new Exceptions\MethodDoesNotSupportException('WeWork doesn\'t support access_token mode');
}
protected function getApiAccessToken(): string
{
return $this->apiAccessToken ?? $this->apiAccessToken = $this->requestApiAccessToken();
}
protected function getUser(string $token, string $code): array
{
$responseInstance = $this->getHttpClient()->get(
$this->baseUrl.'/cgi-bin/user/getuserinfo',
[
'query' => \array_filter(
[
Contracts\RFC6749_ABNF_ACCESS_TOKEN => $token,
Contracts\RFC6749_ABNF_CODE => $code,
]
),
]
);
$response = $this->fromJsonBody($responseInstance);
if (($response['errcode'] ?? 1) > 0 || (empty($response['UserId']) && empty($response['OpenId']))) {
throw new Exceptions\AuthorizeFailedException((string) $responseInstance->getBody(), $response);
} elseif (empty($response['UserId'])) {
$this->detailed = false;
}
return $response;
}
/**
* @throws Exceptions\AuthorizeFailedException
*/
protected function getUserById(string $userId): array
{
$responseInstance = $this->getHttpClient()->post($this->baseUrl.'/cgi-bin/user/get', [
'query' => [
Contracts\RFC6749_ABNF_ACCESS_TOKEN => $this->getApiAccessToken(),
'userid' => $userId,
],
]);
$response = $this->fromJsonBody($responseInstance);
if (($response['errcode'] ?? 1) > 0 || empty($response['userid'])) {
throw new Exceptions\AuthorizeFailedException((string) $responseInstance->getBody(), $response);
}
return $response;
}
#[Pure]
protected function mapUserToObject(array $user): Contracts\UserInterface
{
return new User($this->detailed ? [
Contracts\ABNF_ID => $user['userid'] ?? null,
Contracts\ABNF_NAME => $user[Contracts\ABNF_NAME] ?? null,
Contracts\ABNF_AVATAR => $user[Contracts\ABNF_AVATAR] ?? null,
Contracts\ABNF_EMAIL => $user[Contracts\ABNF_EMAIL] ?? null,
] : [
Contracts\ABNF_ID => $user['UserId'] ?? null ?: $user['OpenId'] ?? null,
]);
}
/**
* @throws Exceptions\AuthorizeFailedException
*/
protected function requestApiAccessToken(): string
{
$responseInstance = $this->getHttpClient()->get($this->baseUrl.'/cgi-bin/gettoken', [
'query' => \array_filter(
[
'corpid' => $this->config->get('corp_id')
?? $this->config->get('corpid')
?? $this->config->get(Contracts\RFC6749_ABNF_CLIENT_ID),
'corpsecret' => $this->config->get('corp_secret')
?? $this->config->get('corpsecret')
?? $this->config->get(Contracts\RFC6749_ABNF_CLIENT_SECRET),
]
),
]);
$response = $this->fromJsonBody($responseInstance);
if (($response['errcode'] ?? 1) > 0) {
throw new Exceptions\AuthorizeFailedException((string) $responseInstance->getBody(), $response);
}
return $response[Contracts\RFC6749_ABNF_ACCESS_TOKEN];
}
protected function getTokenUrl(): string
{
return '';
}
}