Files
2025-05-06 17:51:37 +08:00

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 '';
}
}