| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- <?php
- namespace Overtrue\Socialite\Providers;
- use GuzzleHttp\Client as GuzzleClient;
- use GuzzleHttp\Psr7\Stream;
- use Overtrue\Socialite\Config;
- use Overtrue\Socialite\Contracts\ProviderInterface;
- use Overtrue\Socialite\Exceptions\AuthorizeFailedException;
- use Overtrue\Socialite\Exceptions\MethodDoesNotSupportException;
- use Overtrue\Socialite\User;
- abstract class Base implements ProviderInterface
- {
- public const NAME = null;
- protected ?string $state = null;
- protected Config $config;
- protected ?string $redirectUrl;
- protected array $parameters = [];
- protected array $scopes = [];
- protected string $scopeSeparator = ',';
- protected GuzzleClient $httpClient;
- protected array $guzzleOptions = [];
- protected int $encodingType = PHP_QUERY_RFC1738;
- protected string $expiresInKey = 'expires_in';
- protected string $accessTokenKey = 'access_token';
- protected string $refreshTokenKey = 'refresh_token';
- public function __construct(array $config)
- {
- $this->config = new Config($config);
- // set scopes
- if ($this->config->has('scopes') && is_array($this->config->get('scopes'))) {
- $this->scopes = $this->getConfig()->get('scopes');
- } else if ($this->config->has('scope') && is_string($this->getConfig()->get('scope'))) {
- $this->scopes = array($this->getConfig()->get('scope'));
- }
- // normalize 'client_id'
- if (!$this->config->has('client_id')) {
- $id = $this->config->get('app_id');
- if (null != $id) {
- $this->config->set('client_id', $id);
- }
- }
- // normalize 'client_secret'
- if (!$this->config->has('client_secret')) {
- $secret = $this->config->get('app_secret');
- if (null != $secret) {
- $this->config->set('client_secret', $secret);
- }
- }
- // normalize 'redirect_url'
- if (!$this->config->has('redirect_url')) {
- $this->config->set('redirect_url', $this->config->get('redirect'));
- }
- $this->redirectUrl = $this->config->get('redirect_url');
- }
- abstract protected function getAuthUrl(): string;
- abstract protected function getTokenUrl(): string;
- abstract protected function getUserByToken(string $token): array;
- abstract protected function mapUserToObject(array $user): User;
- /**
- * @param string|null $redirectUrl
- *
- * @return string
- */
- public function redirect(?string $redirectUrl = null): string
- {
- if (!empty($redirectUrl)) {
- $this->withRedirectUrl($redirectUrl);
- }
- return $this->getAuthUrl();
- }
- /**
- * @param string $code
- *
- * @return \Overtrue\Socialite\User
- * @throws \Overtrue\Socialite\Exceptions\AuthorizeFailedException
- * @throws \GuzzleHttp\Exception\GuzzleException
- */
- public function userFromCode(string $code): User
- {
- $tokenResponse = $this->tokenFromCode($code);
- $user = $this->userFromToken($tokenResponse[$this->accessTokenKey]);
- return $user->setRefreshToken($tokenResponse[$this->refreshTokenKey] ?? null)
- ->setExpiresIn($tokenResponse[$this->expiresInKey] ?? null)
- ->setTokenResponse($tokenResponse);
- }
- /**
- * @param string $token
- *
- * @return \Overtrue\Socialite\User
- */
- public function userFromToken(string $token): User
- {
- $user = $this->getUserByToken($token);
- return $this->mapUserToObject($user)->setProvider($this)->setRaw($user)->setAccessToken($token);
- }
- /**
- * @param string $code
- *
- * @return array
- * @throws \Overtrue\Socialite\Exceptions\AuthorizeFailedException|\GuzzleHttp\Exception\GuzzleException
- */
- public function tokenFromCode(string $code): array
- {
- $response = $this->getHttpClient()->post(
- $this->getTokenUrl(),
- [
- 'form_params' => $this->getTokenFields($code),
- 'headers' => [
- 'Accept' => 'application/json',
- ],
- ]
- );
- return $this->normalizeAccessTokenResponse($response->getBody()->getContents());
- }
- /**
- * @param string $refreshToken
- *
- * @throws \Overtrue\Socialite\Exceptions\MethodDoesNotSupportException
- */
- public function refreshToken(string $refreshToken)
- {
- throw new MethodDoesNotSupportException('refreshToken does not support.');
- }
- /**
- * @param string $redirectUrl
- *
- * @return $this|\Overtrue\Socialite\Contracts\ProviderInterface
- */
- public function withRedirectUrl(string $redirectUrl): ProviderInterface
- {
- $this->redirectUrl = $redirectUrl;
- return $this;
- }
- /**
- * @param string $state
- *
- * @return \Overtrue\Socialite\Contracts\ProviderInterface
- */
- public function withState(string $state): ProviderInterface
- {
- $this->state = $state;
- return $this;
- }
- /**
- * @param array $scopes
- *
- * @return $this
- */
- public function scopes(array $scopes): self
- {
- $this->scopes = $scopes;
- return $this;
- }
- /**
- * @param array $parameters
- *
- * @return $this
- */
- public function with(array $parameters): self
- {
- $this->parameters = $parameters;
- return $this;
- }
- public function getConfig(): Config
- {
- return $this->config;
- }
- /**
- * @param string $scopeSeparator
- *
- * @return self
- */
- public function withScopeSeparator(string $scopeSeparator): self
- {
- $this->scopeSeparator = $scopeSeparator;
- return $this;
- }
- public function getClientId(): ?string
- {
- return $this->config->get('client_id');
- }
- public function getClientSecret(): ?string
- {
- return $this->config->get('client_secret');
- }
- public function getHttpClient(): GuzzleClient
- {
- return $this->httpClient ?? new GuzzleClient($this->guzzleOptions);
- }
- /**
- * @param array $config
- *
- * @return \Overtrue\Socialite\Contracts\ProviderInterface
- */
- public function setGuzzleOptions($config = []): ProviderInterface
- {
- $this->guzzleOptions = $config;
- return $this;
- }
- public function getGuzzleOptions(): array
- {
- return $this->guzzleOptions;
- }
- /**
- * @param array $scopes
- * @param string $scopeSeparator
- *
- * @return string
- */
- protected function formatScopes(array $scopes, $scopeSeparator): string
- {
- return implode($scopeSeparator, $scopes);
- }
- /**
- * @param string $code
- *
- * @return array
- */
- protected function getTokenFields(string $code): array
- {
- return [
- 'client_id' => $this->getClientId(),
- 'client_secret' => $this->getClientSecret(),
- 'code' => $code,
- 'redirect_uri' => $this->redirectUrl,
- ];
- }
- /**
- * @param string $url
- *
- * @return string
- */
- protected function buildAuthUrlFromBase(string $url): string
- {
- $query = $this->getCodeFields() + ($this->state ? ['state' => $this->state] : []);
- return $url . '?' . \http_build_query($query, '', '&', $this->encodingType);
- }
- protected function getCodeFields(): array
- {
- $fields = array_merge(
- [
- 'client_id' => $this->getClientId(),
- 'redirect_uri' => $this->redirectUrl,
- 'scope' => $this->formatScopes($this->scopes, $this->scopeSeparator),
- 'response_type' => 'code',
- ],
- $this->parameters
- );
- if ($this->state) {
- $fields['state'] = $this->state;
- }
- return $fields;
- }
- /**
- * @param array|string $response
- *
- * @return mixed
- * @return array
- * @throws \Overtrue\Socialite\Exceptions\AuthorizeFailedException
- *
- */
- protected function normalizeAccessTokenResponse($response): array
- {
- if ($response instanceof Stream) {
- $response->rewind();
- $response = $response->getContents();
- }
- if (\is_string($response)) {
- $response = json_decode($response, true) ?? [];
- }
- if (!\is_array($response)) {
- throw new AuthorizeFailedException('Invalid token response', [$response]);
- }
- if (empty($response[$this->accessTokenKey])) {
- throw new AuthorizeFailedException('Authorize Failed: ' . json_encode($response, JSON_UNESCAPED_UNICODE), $response);
- }
- return $response + [
- 'access_token' => $response[$this->accessTokenKey],
- 'refresh_token' => $response[$this->refreshTokenKey] ?? null,
- 'expires_in' => \intval($response[$this->expiresInKey] ?? 0),
- ];
- }
- }
|