214 lines
6.4 KiB
PHP
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 '';
|
|
}
|
|
}
|