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

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