vendor/hwi/oauth-bundle/src/OAuth/ResourceOwner/GenericOAuth2ResourceOwner.php line 99

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the HWIOAuthBundle package.
  4.  *
  5.  * (c) Hardware Info <opensource@hardware.info>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner;
  11. use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException;
  12. use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken;
  13. use HWI\Bundle\OAuthBundle\Security\Helper\NonceGenerator;
  14. use HWI\Bundle\OAuthBundle\Security\OAuthErrorHandler;
  15. use Symfony\Component\HttpClient\Exception\JsonException;
  16. use Symfony\Component\HttpFoundation\Request as HttpRequest;
  17. use Symfony\Component\OptionsResolver\Options;
  18. use Symfony\Component\OptionsResolver\OptionsResolver;
  19. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  20. use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
  21. /**
  22.  * @author Geoffrey Bachelet <geoffrey.bachelet@gmail.com>
  23.  * @author Alexander <iam.asm89@gmail.com>
  24.  */
  25. abstract class GenericOAuth2ResourceOwner extends AbstractResourceOwner
  26. {
  27.     /**
  28.      * {@inheritdoc}
  29.      */
  30.     public function getUserInformation(array $accessToken, array $extraParameters = [])
  31.     {
  32.         if ($this->options['use_bearer_authorization']) {
  33.             $content $this->httpRequest(
  34.                 $this->normalizeUrl($this->options['infos_url'], $extraParameters),
  35.                 null,
  36.                 ['Authorization' => 'Bearer '.$accessToken['access_token']]
  37.             );
  38.         } else {
  39.             $content $this->doGetUserInformationRequest(
  40.                 $this->normalizeUrl(
  41.                     $this->options['infos_url'],
  42.                     array_merge([$this->options['attr_name'] => $accessToken['access_token']], $extraParameters)
  43.                 )
  44.             );
  45.         }
  46.         try {
  47.             $response $this->getUserResponse();
  48.             $response->setData($content->toArray(false));
  49.             $response->setResourceOwner($this);
  50.             $response->setOAuthToken(new OAuthToken($accessToken));
  51.             return $response;
  52.         } catch (TransportExceptionInterface|JsonException $e) {
  53.             throw new HttpTransportException('Error while sending HTTP request'$this->getName(), $e->getCode(), $e);
  54.         }
  55.     }
  56.     /**
  57.      * {@inheritdoc}
  58.      */
  59.     public function getAuthorizationUrl($redirectUri, array $extraParameters = [])
  60.     {
  61.         if ($this->options['csrf']) {
  62.             $this->handleCsrfToken();
  63.         }
  64.         $parameters array_merge([
  65.             'response_type' => 'code',
  66.             'client_id' => $this->options['client_id'],
  67.             'scope' => $this->options['scope'],
  68.             'state' => $this->state->encode(),
  69.             'redirect_uri' => $redirectUri,
  70.         ], $extraParameters);
  71.         return $this->normalizeUrl($this->options['authorization_url'], $parameters);
  72.     }
  73.     /**
  74.      * {@inheritdoc}
  75.      */
  76.     public function getAccessToken(HttpRequest $request$redirectUri, array $extraParameters = [])
  77.     {
  78.         OAuthErrorHandler::handleOAuthError($request);
  79.         $parameters array_merge([
  80.             'code' => $request->query->get('code'),
  81.             'grant_type' => 'authorization_code',
  82.             'redirect_uri' => $redirectUri,
  83.         ], $extraParameters);
  84.         $response $this->doGetTokenRequest($this->options['access_token_url'], $parameters);
  85.         $response $this->getResponseContent($response);
  86.         $this->validateResponseContent($response);
  87.         return $response;
  88.     }
  89.     /**
  90.      * {@inheritdoc}
  91.      */
  92.     public function refreshAccessToken($refreshToken, array $extraParameters = [])
  93.     {
  94.         $parameters array_merge([
  95.             'refresh_token' => $refreshToken,
  96.             'grant_type' => 'refresh_token',
  97.         ], $extraParameters);
  98.         $response $this->doGetTokenRequest($this->options['access_token_url'], $parameters);
  99.         $response $this->getResponseContent($response);
  100.         $this->validateResponseContent($response);
  101.         return $response;
  102.     }
  103.     /**
  104.      * {@inheritdoc}
  105.      */
  106.     public function revokeToken($token)
  107.     {
  108.         if (!isset($this->options['revoke_token_url'])) {
  109.             throw new AuthenticationException('OAuth error: "Method unsupported."');
  110.         }
  111.         $parameters = [
  112.             'client_id' => $this->options['client_id'],
  113.             'client_secret' => $this->options['client_secret'],
  114.         ];
  115.         $response $this->httpRequest($this->normalizeUrl($this->options['revoke_token_url'], ['token' => $token]), $parameters, [], 'DELETE');
  116.         return 200 === $response->getStatusCode();
  117.     }
  118.     /**
  119.      * {@inheritdoc}
  120.      */
  121.     public function handles(HttpRequest $request)
  122.     {
  123.         return $request->query->has('code');
  124.     }
  125.     /**
  126.      * {@inheritdoc}
  127.      */
  128.     public function isCsrfTokenValid($csrfToken)
  129.     {
  130.         // Mark token valid when validation is disabled
  131.         if (!$this->options['csrf']) {
  132.             return true;
  133.         }
  134.         if (null === $csrfToken) {
  135.             throw new AuthenticationException('Given CSRF token is not valid.');
  136.         }
  137.         try {
  138.             return null !== $this->storage->fetch($thisurldecode($csrfToken), 'csrf_state');
  139.         } catch (\InvalidArgumentException $e) {
  140.             throw new AuthenticationException('Given CSRF token is not valid.');
  141.         }
  142.     }
  143.     /**
  144.      * {@inheritdoc}
  145.      */
  146.     public function shouldRefreshOnExpire()
  147.     {
  148.         return $this->options['refresh_on_expire'] ?? false;
  149.     }
  150.     /**
  151.      * {@inheritdoc}
  152.      */
  153.     protected function doGetTokenRequest($url, array $parameters = [])
  154.     {
  155.         $headers = [];
  156.         if ($this->options['use_authorization_to_get_token']) {
  157.             if ($this->options['client_secret']) {
  158.                 $headers['Authorization'] = 'Basic '.base64_encode($this->options['client_id'].':'.$this->options['client_secret']);
  159.             }
  160.         } else {
  161.             $parameters['client_id'] = $this->options['client_id'];
  162.             $parameters['client_secret'] = $this->options['client_secret'];
  163.         }
  164.         return $this->httpRequest($urlhttp_build_query($parameters'''&'), $headers);
  165.     }
  166.     /**
  167.      * {@inheritdoc}
  168.      */
  169.     protected function doGetUserInformationRequest($url, array $parameters = [])
  170.     {
  171.         return $this->httpRequest($urlhttp_build_query($parameters'''&'));
  172.     }
  173.     /**
  174.      * @param mixed $response the 'parsed' content based on the response headers
  175.      *
  176.      * @throws AuthenticationException If an OAuth error occurred or no access token is found
  177.      */
  178.     protected function validateResponseContent($response)
  179.     {
  180.         if (isset($response['error_description'])) {
  181.             throw new AuthenticationException(sprintf('OAuth error: "%s"'$response['error_description']));
  182.         }
  183.         if (isset($response['error'])) {
  184.             throw new AuthenticationException(sprintf('OAuth error: "%s"'$response['error']['message'] ?? $response['error']));
  185.         }
  186.         if (!isset($response['access_token'])) {
  187.             throw new AuthenticationException('Not a valid access token.');
  188.         }
  189.     }
  190.     /**
  191.      * {@inheritdoc}
  192.      */
  193.     protected function configureOptions(OptionsResolver $resolver)
  194.     {
  195.         parent::configureOptions($resolver);
  196.         $resolver->setDefaults([
  197.             'attr_name' => 'access_token',
  198.             'use_commas_in_scope' => false,
  199.             'use_bearer_authorization' => true,
  200.             'use_authorization_to_get_token' => true,
  201.             'refresh_on_expire' => false,
  202.         ]);
  203.         $resolver->setDefined('revoke_token_url');
  204.         $resolver->setAllowedValues('refresh_on_expire', [truefalse]);
  205.         // Unfortunately some resource owners break the spec by using commas instead
  206.         // of spaces to separate scopes (Disqus, Facebook, Github, Vkontante)
  207.         $scopeNormalizer = function (Options $options$value) {
  208.             if (!$value) {
  209.                 return null;
  210.             }
  211.             if (!$options['use_commas_in_scope']) {
  212.                 return $value;
  213.             }
  214.             return str_replace(','' '$value);
  215.         };
  216.         $resolver->setNormalizer('scope'$scopeNormalizer);
  217.     }
  218.     /**
  219.      * {@inheritdoc}
  220.      */
  221.     protected function httpRequest($url$content null, array $headers = [], $method null)
  222.     {
  223.         $headers += ['Content-Type' => 'application/x-www-form-urlencoded'];
  224.         return parent::httpRequest($url$content$headers$method);
  225.     }
  226.     private function handleCsrfToken(): void
  227.     {
  228.         if (null === $this->state->getCsrfToken()) {
  229.             $this->state->setCsrfToken(NonceGenerator::generate());
  230.         }
  231.         $this->storage->save($this$this->state->getCsrfToken(), 'csrf_state');
  232.     }
  233. }