vendor/knpuniversity/oauth2-client-bundle/src/Client/OAuth2Client.php line 72

Open in your IDE?
  1. <?php
  2. /*
  3.  * OAuth2 Client Bundle
  4.  * Copyright (c) KnpUniversity <http://knpuniversity.com/>
  5.  *
  6.  * For the full copyright and license information, please view the LICENSE
  7.  * file that was distributed with this source code.
  8.  */
  9. namespace KnpU\OAuth2ClientBundle\Client;
  10. use KnpU\OAuth2ClientBundle\Exception\InvalidStateException;
  11. use KnpU\OAuth2ClientBundle\Exception\MissingAuthorizationCodeException;
  12. use League\OAuth2\Client\Provider\AbstractProvider;
  13. use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
  14. use League\OAuth2\Client\Token\AccessToken;
  15. use Symfony\Component\HttpFoundation\RedirectResponse;
  16. use Symfony\Component\HttpFoundation\RequestStack;
  17. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  18. class OAuth2Client implements OAuth2ClientInterface
  19. {
  20.     public const OAUTH2_SESSION_STATE_KEY 'knpu.oauth2_client_state';
  21.     private AbstractProvider $provider;
  22.     private RequestStack $requestStack;
  23.     private bool $isStateless false;
  24.     /**
  25.      * OAuth2Client constructor.
  26.      */
  27.     public function __construct(AbstractProvider $providerRequestStack $requestStack)
  28.     {
  29.         $this->provider $provider;
  30.         $this->requestStack $requestStack;
  31.     }
  32.     /**
  33.      * Call this to avoid using and checking "state".
  34.      */
  35.     public function setAsStateless()
  36.     {
  37.         $this->isStateless true;
  38.     }
  39.     /**
  40.      * Creates a RedirectResponse that will send the user to the
  41.      * OAuth2 server (e.g. send them to Facebook).
  42.      *
  43.      * @param array $scopes  The scopes you want (leave empty to use default)
  44.      * @param array $options Extra options to pass to the Provider's getAuthorizationUrl()
  45.      *                       method. For example, <code>scope</code> is a common option.
  46.      *                       Generally, these become query parameters when redirecting.
  47.      *
  48.      * @return RedirectResponse
  49.      */
  50.     public function redirect(array $scopes = [], array $options = [])
  51.     {
  52.         if (!empty($scopes)) {
  53.             $options['scope'] = $scopes;
  54.         }
  55.         $url $this->provider->getAuthorizationUrl($options);
  56.         // set the state (unless we're stateless)
  57.         if (!$this->isStateless()) {
  58.             $this->getSession()->set(
  59.                 self::OAUTH2_SESSION_STATE_KEY,
  60.                 $this->provider->getState()
  61.             );
  62.         }
  63.         return new RedirectResponse($url);
  64.     }
  65.     /**
  66.      * Call this after the user is redirected back to get the access token.
  67.      *
  68.      * @param array $options Additional options that should be passed to the getAccessToken() of the underlying provider
  69.      *
  70.      * @return AccessToken|\League\OAuth2\Client\Token\AccessTokenInterface
  71.      *
  72.      * @throws InvalidStateException
  73.      * @throws MissingAuthorizationCodeException
  74.      * @throws IdentityProviderException         If token cannot be fetched
  75.      */
  76.     public function getAccessToken(array $options = [])
  77.     {
  78.         if (!$this->isStateless()) {
  79.             $expectedState $this->getSession()->get(self::OAUTH2_SESSION_STATE_KEY);
  80.             $actualState $this->getCurrentRequest()->get('state');
  81.             if (!$actualState || ($actualState !== $expectedState)) {
  82.                 throw new InvalidStateException('Invalid state');
  83.             }
  84.         }
  85.         $code $this->getCurrentRequest()->get('code');
  86.         if (!$code) {
  87.             throw new MissingAuthorizationCodeException('No "code" parameter was found (usually this is a query parameter)!');
  88.         }
  89.         return $this->provider->getAccessToken(
  90.             'authorization_code',
  91.             array_merge(['code' => $code], $options)
  92.         );
  93.     }
  94.     /**
  95.      * Get a new AccessToken from a refresh token.
  96.      *
  97.      * @param array $options Additional options that should be passed to the getAccessToken() of the underlying provider
  98.      *
  99.      * @return AccessToken|\League\OAuth2\Client\Token\AccessTokenInterface
  100.      *
  101.      * @throws IdentityProviderException If token cannot be fetched
  102.      */
  103.     public function refreshAccessToken(string $refreshToken, array $options = [])
  104.     {
  105.         return $this->provider->getAccessToken(
  106.             'refresh_token',
  107.             array_merge(['refresh_token' => $refreshToken], $options)
  108.         );
  109.     }
  110.     /**
  111.      * Returns the "User" information (called a resource owner).
  112.      *
  113.      * @return \League\OAuth2\Client\Provider\ResourceOwnerInterface
  114.      */
  115.     public function fetchUserFromToken(AccessToken $accessToken)
  116.     {
  117.         return $this->provider->getResourceOwner($accessToken);
  118.     }
  119.     /**
  120.      * Shortcut to fetch the access token and user all at once.
  121.      *
  122.      * Only use this if you don't need the access token, but only
  123.      * need the user.
  124.      *
  125.      * @return \League\OAuth2\Client\Provider\ResourceOwnerInterface
  126.      */
  127.     public function fetchUser()
  128.     {
  129.         /** @var AccessToken $token */
  130.         $token $this->getAccessToken();
  131.         return $this->fetchUserFromToken($token);
  132.     }
  133.     /**
  134.      * Returns the underlying OAuth2 provider.
  135.      *
  136.      * @return AbstractProvider
  137.      */
  138.     public function getOAuth2Provider()
  139.     {
  140.         return $this->provider;
  141.     }
  142.     protected function isStateless(): bool
  143.     {
  144.         return $this->isStateless;
  145.     }
  146.     /**
  147.      * @return \Symfony\Component\HttpFoundation\Request
  148.      */
  149.     private function getCurrentRequest()
  150.     {
  151.         $request $this->requestStack->getCurrentRequest();
  152.         if (!$request) {
  153.             throw new \LogicException('There is no "current request", and it is needed to perform this action');
  154.         }
  155.         return $request;
  156.     }
  157.     /**
  158.      * @return SessionInterface
  159.      */
  160.     private function getSession()
  161.     {
  162.         if (!$this->getCurrentRequest()->hasSession()) {
  163.             throw new \LogicException('In order to use "state", you must have a session. Set the OAuth2Client to stateless to avoid state');
  164.         }
  165.         return $this->getCurrentRequest()->getSession();
  166.     }
  167. }