254 lines
7.4 KiB
PHP
Executable File
254 lines
7.4 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Overtrue\Socialite\Providers;
|
|
|
|
use GuzzleHttp\Exception\GuzzleException;
|
|
use JetBrains\PhpStorm\Pure;
|
|
use Overtrue\Socialite\Contracts;
|
|
use Overtrue\Socialite\Exceptions;
|
|
use Overtrue\Socialite\Exceptions\AuthorizeFailedException;
|
|
use Overtrue\Socialite\User;
|
|
|
|
/**
|
|
* @link https://open.work.weixin.qq.com/api/doc/90001/90143/91120
|
|
*/
|
|
class OpenWeWork extends Base
|
|
{
|
|
public const NAME = 'open-wework';
|
|
|
|
protected bool $detailed = false;
|
|
|
|
protected bool $asQrcode = false;
|
|
|
|
protected string $userType = 'member';
|
|
|
|
protected string $lang = 'zh';
|
|
|
|
protected ?string $suiteTicket = null;
|
|
|
|
protected ?int $agentId = null;
|
|
|
|
protected ?string $suiteAccessToken = null;
|
|
|
|
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');
|
|
}
|
|
}
|
|
|
|
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 withUserType(string $userType): self
|
|
{
|
|
$this->userType = $userType;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function withLang(string $lang): self
|
|
{
|
|
$this->lang = $lang;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @throws GuzzleException
|
|
* @throws AuthorizeFailedException
|
|
*/
|
|
public function userFromCode(string $code): Contracts\UserInterface
|
|
{
|
|
$user = $this->getUser($this->getSuiteAccessToken(), $code);
|
|
|
|
if ($this->detailed) {
|
|
$user = \array_merge($user, $this->getUserByTicket($user['user_ticket']));
|
|
}
|
|
|
|
return $this->mapUserToObject($user)->setProvider($this)->setRaw($user);
|
|
}
|
|
|
|
public function withSuiteTicket(string $suiteTicket): self
|
|
{
|
|
$this->suiteTicket = $suiteTicket;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function withSuiteAccessToken(string $suiteAccessToken): self
|
|
{
|
|
$this->suiteAccessToken = $suiteAccessToken;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @throws Exceptions\InvalidArgumentException
|
|
*/
|
|
public function getAuthUrl(): string
|
|
{
|
|
$queries = \array_filter([
|
|
'appid' => $this->getClientId(),
|
|
Contracts\RFC6749_ABNF_REDIRECT_URI => $this->redirectUrl,
|
|
Contracts\RFC6749_ABNF_RESPONSE_TYPE => Contracts\RFC6749_ABNF_CODE,
|
|
Contracts\RFC6749_ABNF_SCOPE => $this->formatScopes($this->scopes, $this->scopeSeparator),
|
|
Contracts\RFC6749_ABNF_STATE => $this->state,
|
|
'agentid' => $this->agentId,
|
|
]);
|
|
|
|
if ($this->asQrcode) {
|
|
$queries = array_filter([
|
|
'appid' => $queries['appid'] ?? $this->getClientId(),
|
|
'redirect_uri' => $queries['redirect_uri'] ?? $this->redirectUrl,
|
|
'usertype' => $this->userType,
|
|
'lang' => $this->lang,
|
|
'state' => $this->state,
|
|
]);
|
|
|
|
return \sprintf('https://open.work.weixin.qq.com/wwopen/sso/3rd_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('Open WeWork doesn\'t support access_token mode');
|
|
}
|
|
|
|
protected function getSuiteAccessToken(): string
|
|
{
|
|
return $this->suiteAccessToken ?? $this->suiteAccessToken = $this->requestSuiteAccessToken();
|
|
}
|
|
|
|
/**
|
|
* @throws Exceptions\AuthorizeFailedException|GuzzleException
|
|
*/
|
|
protected function getUser(string $token, string $code): array
|
|
{
|
|
$responseInstance = $this->getHttpClient()->get(
|
|
$this->baseUrl.'/cgi-bin/service/getuserinfo3rd',
|
|
[
|
|
'query' => \array_filter(
|
|
[
|
|
'suite_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['user_ticket'])) {
|
|
$this->detailed = false;
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* @throws Exceptions\AuthorizeFailedException
|
|
* @throws GuzzleException
|
|
*/
|
|
protected function getUserByTicket(string $userTicket): array
|
|
{
|
|
$responseInstance = $this->getHttpClient()->post(
|
|
$this->baseUrl.'/cgi-bin/service/auth/getuserdetail3rd',
|
|
[
|
|
'query' => [
|
|
'suite_access_token' => $this->getSuiteAccessToken(),
|
|
],
|
|
'json' => [
|
|
'user_ticket' => $userTicket,
|
|
],
|
|
],
|
|
);
|
|
|
|
$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'] ?? $user['UserId'] ?? null,
|
|
Contracts\ABNF_NAME => $user[Contracts\ABNF_NAME] ?? null,
|
|
Contracts\ABNF_AVATAR => $user[Contracts\ABNF_AVATAR] ?? null,
|
|
'gender' => $user['gender'] ?? null,
|
|
'corpid' => $user['corpid'] ?? $user['CorpId'] ?? null,
|
|
'open_userid' => $user['open_userid'] ?? null,
|
|
'qr_code' => $user['qr_code'] ?? null,
|
|
] : [
|
|
Contracts\ABNF_ID => $user['userid'] ?? $user['UserId'] ?? $user['OpenId'] ?? $user['openid'] ?? null,
|
|
'corpid' => $user['CorpId'] ?? null,
|
|
'open_userid' => $user['open_userid'] ?? null,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @throws Exceptions\AuthorizeFailedException
|
|
* @throws GuzzleException
|
|
*/
|
|
protected function requestSuiteAccessToken(): string
|
|
{
|
|
$responseInstance = $this->getHttpClient()->post(
|
|
$this->baseUrl.'/cgi-bin/service/get_suite_token',
|
|
[
|
|
'json' => [
|
|
'suite_id' => $this->config->get('suite_id') ?? $this->config->get('client_id'),
|
|
'suite_secret' => $this->config->get('suite_secret') ?? $this->config->get('client_secret'),
|
|
'suite_ticket' => $this->suiteTicket,
|
|
],
|
|
]
|
|
);
|
|
|
|
$response = $this->fromJsonBody($responseInstance);
|
|
|
|
if (isset($response['errcode']) && $response['errcode'] > 0) {
|
|
throw new Exceptions\AuthorizeFailedException((string) $responseInstance->getBody(), $response);
|
|
}
|
|
|
|
return $response['suite_access_token'];
|
|
}
|
|
|
|
protected function getTokenUrl(): string
|
|
{
|
|
return '';
|
|
}
|
|
}
|