| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- <?php
- namespace Overtrue\Socialite\Providers;
- use GuzzleHttp\Exception\GuzzleException;
- use Overtrue\Socialite\Exceptions\AuthorizeFailedException;
- use Overtrue\Socialite\Exceptions\BadRequestException;
- use Overtrue\Socialite\Exceptions\Feishu\InvalidTicketException;
- use Overtrue\Socialite\Exceptions\InvalidTokenException;
- use Overtrue\Socialite\User;
- /**
- * @see https://open.feishu.cn/document/uQjL04CN/ucDOz4yN4MjL3gzM
- */
- class FeiShu extends Base
- {
- public const NAME = 'feishu';
- protected string $baseUrl = 'https://open.feishu.cn/open-apis';
- protected string $expiresInKey = 'refresh_expires_in';
- protected bool $isInternalApp = false;
- public function __construct(array $config)
- {
- parent::__construct($config);
- $this->isInternalApp = ($this->config->get('app_mode') ?? $this->config->get('mode')) == 'internal';
- }
- protected function getAuthUrl(): string
- {
- return $this->buildAuthUrlFromBase($this->baseUrl . '/authen/v1/index');
- }
- protected function getCodeFields(): array
- {
- return [
- 'redirect_uri' => $this->redirectUrl,
- 'app_id' => $this->getClientId(),
- ];
- }
- protected function getTokenUrl(): string
- {
- return $this->baseUrl . '/authen/v1/access_token';
- }
- /**
- * @param string $code
- *
- * @return array
- * @throws AuthorizeFailedException
- * @throws GuzzleException
- */
- public function tokenFromCode(string $code): array
- {
- return $this->normalizeAccessTokenResponse($this->getTokenFromCode($code));
- }
- /**
- * @param string $code
- *
- * @return array
- * @throws AuthorizeFailedException
- *
- * @throws AuthorizeFailedException
- * @throws GuzzleException
- */
- protected function getTokenFromCode(string $code): array
- {
- $this->configAppAccessToken();
- $response = $this->getHttpClient()->post(
- $this->getTokenUrl(),
- [
- 'json' => [
- 'app_access_token' => $this->config->get('app_access_token'),
- 'code' => $code,
- 'grant_type' => 'authorization_code',
- ],
- ]
- );
- $response = \json_decode($response->getBody(), true) ?? [];
- if (empty($response['data'])) {
- throw new AuthorizeFailedException('Invalid token response', $response);
- }
- return $this->normalizeAccessTokenResponse($response['data']);
- }
- /**
- * @param string $token
- *
- * @return array
- * @throws GuzzleException
- */
- protected function getUserByToken(string $token): array
- {
- $response = $this->getHttpClient()->get(
- $this->baseUrl . '/authen/v1/user_info',
- [
- 'headers' => ['Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $token],
- 'query' => array_filter(
- [
- 'user_access_token' => $token,
- ]
- ),
- ]
- );
- $response = \json_decode($response->getBody(), true) ?? [];
- if (empty($response['data'])) {
- throw new \InvalidArgumentException('You have error! ' . json_encode($response, JSON_UNESCAPED_UNICODE));
- }
- return $response['data'];
- }
- /**
- * @param array $user
- *
- * @return User
- */
- protected function mapUserToObject(array $user): User
- {
- return new User(
- [
- 'id' => $user['user_id'] ?? null,
- 'name' => $user['name'] ?? null,
- 'nickname' => $user['name'] ?? null,
- 'avatar' => $user['avatar_url'] ?? null,
- 'email' => $user['email'] ?? null,
- ]
- );
- }
- public function withInternalAppMode(): self
- {
- $this->isInternalApp = true;
- return $this;
- }
- public function withDefaultMode(): self
- {
- $this->isInternalApp = false;
- return $this;
- }
- /**
- * set 'app_ticket' in config attribute
- *
- * @param string $appTicket
- *
- * @return FeiShu
- */
- public function withAppTicket(string $appTicket): self
- {
- $this->config->set('app_ticket', $appTicket);
- return $this;
- }
- /**
- * 设置 app_access_token 到 config 设置中
- * 应用维度授权凭证,开放平台可据此识别调用方的应用身份
- * 分内建和自建
- */
- protected function configAppAccessToken()
- {
- $url = $this->baseUrl . '/auth/v3/app_access_token/';
- $params = [
- 'json' => [
- 'app_id' => $this->config->get('client_id'),
- 'app_secret' => $this->config->get('client_secret'),
- 'app_ticket' => $this->config->get('app_ticket'),
- ],
- ];
- if ($this->isInternalApp) {
- $url = $this->baseUrl . '/auth/v3/app_access_token/internal/';
- $params = [
- 'json' => [
- 'app_id' => $this->config->get('client_id'),
- 'app_secret' => $this->config->get('client_secret'),
- ],
- ];
- }
- if (!$this->isInternalApp && !$this->config->has('app_ticket')) {
- throw new InvalidTicketException('You are using default mode, please config \'app_ticket\' first');
- }
- $response = $this->getHttpClient()->post($url, $params);
- $response = \json_decode($response->getBody(), true) ?? [];
- if (empty($response['app_access_token'])) {
- throw new InvalidTokenException('Invalid \'app_access_token\' response', json_encode($response));
- }
- $this->config->set('app_access_token', $response['app_access_token']);
- }
- /**
- * 设置 tenant_access_token 到 config 属性中
- * 应用的企业授权凭证,开放平台据此识别调用方的应用身份和企业身份
- * 分内建和自建
- */
- protected function configTenantAccessToken()
- {
- $url = $this->baseUrl . '/auth/v3/tenant_access_token/';
- $params = [
- 'json' => [
- 'app_id' => $this->config->get('client_id'),
- 'app_secret' => $this->config->get('client_secret'),
- 'app_ticket' => $this->config->get('app_ticket'),
- ],
- ];
- if ($this->isInternalApp) {
- $url = $this->baseUrl . '/auth/v3/tenant_access_token/internal/';
- $params = [
- 'json' => [
- 'app_id' => $this->config->get('client_id'),
- 'app_secret' => $this->config->get('client_secret'),
- ],
- ];
- }
- if (!$this->isInternalApp && !$this->config->has('app_ticket')) {
- throw new BadRequestException('You are using default mode, please config \'app_ticket\' first');
- }
- $response = $this->getHttpClient()->post($url, $params);
- $response = \json_decode($response->getBody(), true) ?? [];
- if (empty($response['tenant_access_token'])) {
- throw new AuthorizeFailedException('Invalid tenant_access_token response', $response);
- }
- $this->config->set('tenant_access_token', $response['tenant_access_token']);
- }
- }
|