vendor/league/oauth2-client/src/Provider/AbstractProvider.php line 704

Open in your IDE?
  1. <?php
  2. /**
  3.  * This file is part of the league/oauth2-client library
  4.  *
  5.  * For the full copyright and license information, please view the LICENSE
  6.  * file that was distributed with this source code.
  7.  *
  8.  * @copyright Copyright (c) Alex Bilbie <[email protected]>
  9.  * @license http://opensource.org/licenses/MIT MIT
  10.  * @link http://thephpleague.com/oauth2-client/ Documentation
  11.  * @link https://packagist.org/packages/league/oauth2-client Packagist
  12.  * @link https://github.com/thephpleague/oauth2-client GitHub
  13.  */
  14. namespace League\OAuth2\Client\Provider;
  15. use GuzzleHttp\Client as HttpClient;
  16. use GuzzleHttp\ClientInterface as HttpClientInterface;
  17. use GuzzleHttp\Exception\BadResponseException;
  18. use InvalidArgumentException;
  19. use League\OAuth2\Client\Grant\AbstractGrant;
  20. use League\OAuth2\Client\Grant\GrantFactory;
  21. use League\OAuth2\Client\OptionProvider\OptionProviderInterface;
  22. use League\OAuth2\Client\OptionProvider\PostAuthOptionProvider;
  23. use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
  24. use League\OAuth2\Client\Token\AccessToken;
  25. use League\OAuth2\Client\Token\AccessTokenInterface;
  26. use League\OAuth2\Client\Tool\ArrayAccessorTrait;
  27. use League\OAuth2\Client\Tool\GuardedPropertyTrait;
  28. use League\OAuth2\Client\Tool\QueryBuilderTrait;
  29. use League\OAuth2\Client\Tool\RequestFactory;
  30. use Psr\Http\Message\RequestInterface;
  31. use Psr\Http\Message\ResponseInterface;
  32. use UnexpectedValueException;
  33. /**
  34.  * Represents a service provider (authorization server).
  35.  *
  36.  * @link http://tools.ietf.org/html/rfc6749#section-1.1 Roles (RFC 6749, ยง1.1)
  37.  */
  38. abstract class AbstractProvider
  39. {
  40.     use ArrayAccessorTrait;
  41.     use GuardedPropertyTrait;
  42.     use QueryBuilderTrait;
  43.     /**
  44.      * @var string|null Key used in a token response to identify the resource owner.
  45.      */
  46.     const ACCESS_TOKEN_RESOURCE_OWNER_ID null;
  47.     /**
  48.      * @var string HTTP method used to fetch access tokens.
  49.      */
  50.     const METHOD_GET 'GET';
  51.     /**
  52.      * @var string HTTP method used to fetch access tokens.
  53.      */
  54.     const METHOD_POST 'POST';
  55.     /**
  56.      * @var string PKCE method used to fetch authorization token.
  57.      * The PKCE code challenge will be hashed with sha256 (recommended).
  58.      */
  59.     const PKCE_METHOD_S256 'S256';
  60.     /**
  61.      * @var string PKCE method used to fetch authorization token.
  62.      * The PKCE code challenge will be sent as plain text, this is NOT recommended.
  63.      * Only use `plain` if no other option is possible.
  64.      */
  65.     const PKCE_METHOD_PLAIN 'plain';
  66.     /**
  67.      * @var string
  68.      */
  69.     protected $clientId;
  70.     /**
  71.      * @var string
  72.      */
  73.     protected $clientSecret;
  74.     /**
  75.      * @var string
  76.      */
  77.     protected $redirectUri;
  78.     /**
  79.      * @var string
  80.      */
  81.     protected $state;
  82.     /**
  83.      * @var string|null
  84.      */
  85.     protected $pkceCode null;
  86.     /**
  87.      * @var GrantFactory
  88.      */
  89.     protected $grantFactory;
  90.     /**
  91.      * @var RequestFactory
  92.      */
  93.     protected $requestFactory;
  94.     /**
  95.      * @var HttpClientInterface
  96.      */
  97.     protected $httpClient;
  98.     /**
  99.      * @var OptionProviderInterface
  100.      */
  101.     protected $optionProvider;
  102.     /**
  103.      * Constructs an OAuth 2.0 service provider.
  104.      *
  105.      * @param array $options An array of options to set on this provider.
  106.      *     Options include `clientId`, `clientSecret`, `redirectUri`, and `state`.
  107.      *     Individual providers may introduce more options, as needed.
  108.      * @param array $collaborators An array of collaborators that may be used to
  109.      *     override this provider's default behavior. Collaborators include
  110.      *     `grantFactory`, `requestFactory`, and `httpClient`.
  111.      *     Individual providers may introduce more collaborators, as needed.
  112.      */
  113.     public function __construct(array $options = [], array $collaborators = [])
  114.     {
  115.         // We'll let the GuardedPropertyTrait handle mass assignment of incoming
  116.         // options, skipping any blacklisted properties defined in the provider
  117.         $this->fillProperties($options);
  118.         if (empty($collaborators['grantFactory'])) {
  119.             $collaborators['grantFactory'] = new GrantFactory();
  120.         }
  121.         $this->setGrantFactory($collaborators['grantFactory']);
  122.         if (empty($collaborators['requestFactory'])) {
  123.             $collaborators['requestFactory'] = new RequestFactory();
  124.         }
  125.         $this->setRequestFactory($collaborators['requestFactory']);
  126.         if (empty($collaborators['httpClient'])) {
  127.             $client_options $this->getAllowedClientOptions($options);
  128.             $collaborators['httpClient'] = new HttpClient(
  129.                 array_intersect_key($optionsarray_flip($client_options))
  130.             );
  131.         }
  132.         $this->setHttpClient($collaborators['httpClient']);
  133.         if (empty($collaborators['optionProvider'])) {
  134.             $collaborators['optionProvider'] = new PostAuthOptionProvider();
  135.         }
  136.         $this->setOptionProvider($collaborators['optionProvider']);
  137.     }
  138.     /**
  139.      * Returns the list of options that can be passed to the HttpClient
  140.      *
  141.      * @param array $options An array of options to set on this provider.
  142.      *     Options include `clientId`, `clientSecret`, `redirectUri`, and `state`.
  143.      *     Individual providers may introduce more options, as needed.
  144.      * @return array The options to pass to the HttpClient constructor
  145.      */
  146.     protected function getAllowedClientOptions(array $options)
  147.     {
  148.         $client_options = ['timeout''proxy'];
  149.         // Only allow turning off ssl verification if it's for a proxy
  150.         if (!empty($options['proxy'])) {
  151.             $client_options[] = 'verify';
  152.         }
  153.         return $client_options;
  154.     }
  155.     /**
  156.      * Sets the grant factory instance.
  157.      *
  158.      * @param  GrantFactory $factory
  159.      * @return self
  160.      */
  161.     public function setGrantFactory(GrantFactory $factory)
  162.     {
  163.         $this->grantFactory $factory;
  164.         return $this;
  165.     }
  166.     /**
  167.      * Returns the current grant factory instance.
  168.      *
  169.      * @return GrantFactory
  170.      */
  171.     public function getGrantFactory()
  172.     {
  173.         return $this->grantFactory;
  174.     }
  175.     /**
  176.      * Sets the request factory instance.
  177.      *
  178.      * @param  RequestFactory $factory
  179.      * @return self
  180.      */
  181.     public function setRequestFactory(RequestFactory $factory)
  182.     {
  183.         $this->requestFactory $factory;
  184.         return $this;
  185.     }
  186.     /**
  187.      * Returns the request factory instance.
  188.      *
  189.      * @return RequestFactory
  190.      */
  191.     public function getRequestFactory()
  192.     {
  193.         return $this->requestFactory;
  194.     }
  195.     /**
  196.      * Sets the HTTP client instance.
  197.      *
  198.      * @param  HttpClientInterface $client
  199.      * @return self
  200.      */
  201.     public function setHttpClient(HttpClientInterface $client)
  202.     {
  203.         $this->httpClient $client;
  204.         return $this;
  205.     }
  206.     /**
  207.      * Returns the HTTP client instance.
  208.      *
  209.      * @return HttpClientInterface
  210.      */
  211.     public function getHttpClient()
  212.     {
  213.         return $this->httpClient;
  214.     }
  215.     /**
  216.      * Sets the option provider instance.
  217.      *
  218.      * @param  OptionProviderInterface $provider
  219.      * @return self
  220.      */
  221.     public function setOptionProvider(OptionProviderInterface $provider)
  222.     {
  223.         $this->optionProvider $provider;
  224.         return $this;
  225.     }
  226.     /**
  227.      * Returns the option provider instance.
  228.      *
  229.      * @return OptionProviderInterface
  230.      */
  231.     public function getOptionProvider()
  232.     {
  233.         return $this->optionProvider;
  234.     }
  235.     /**
  236.      * Returns the current value of the state parameter.
  237.      *
  238.      * This can be accessed by the redirect handler during authorization.
  239.      *
  240.      * @return string
  241.      */
  242.     public function getState()
  243.     {
  244.         return $this->state;
  245.     }
  246.     /**
  247.      * Set the value of the pkceCode parameter.
  248.      *
  249.      * When using PKCE this should be set before requesting an access token.
  250.      *
  251.      * @param string $pkceCode
  252.      * @return self
  253.      */
  254.     public function setPkceCode($pkceCode)
  255.     {
  256.         $this->pkceCode $pkceCode;
  257.         return $this;
  258.     }
  259.     /**
  260.      * Returns the current value of the pkceCode parameter.
  261.      *
  262.      * This can be accessed by the redirect handler during authorization.
  263.      *
  264.      * @return string|null
  265.      */
  266.     public function getPkceCode()
  267.     {
  268.         return $this->pkceCode;
  269.     }
  270.     /**
  271.      * Returns the base URL for authorizing a client.
  272.      *
  273.      * Eg. https://oauth.service.com/authorize
  274.      *
  275.      * @return string
  276.      */
  277.     abstract public function getBaseAuthorizationUrl();
  278.     /**
  279.      * Returns the base URL for requesting an access token.
  280.      *
  281.      * Eg. https://oauth.service.com/token
  282.      *
  283.      * @param array $params
  284.      * @return string
  285.      */
  286.     abstract public function getBaseAccessTokenUrl(array $params);
  287.     /**
  288.      * Returns the URL for requesting the resource owner's details.
  289.      *
  290.      * @param AccessToken $token
  291.      * @return string
  292.      */
  293.     abstract public function getResourceOwnerDetailsUrl(AccessToken $token);
  294.     /**
  295.      * Returns a new random string to use as the state parameter in an
  296.      * authorization flow.
  297.      *
  298.      * @param  int $length Length of the random string to be generated.
  299.      * @return string
  300.      */
  301.     protected function getRandomState($length 32)
  302.     {
  303.         // Converting bytes to hex will always double length. Hence, we can reduce
  304.         // the amount of bytes by half to produce the correct length.
  305.         return bin2hex(random_bytes($length 2));
  306.     }
  307.     /**
  308.      * Returns a new random string to use as PKCE code_verifier and
  309.      * hashed as code_challenge parameters in an authorization flow.
  310.      * Must be between 43 and 128 characters long.
  311.      *
  312.      * @param  int $length Length of the random string to be generated.
  313.      * @return string
  314.      */
  315.     protected function getRandomPkceCode($length 64)
  316.     {
  317.         return substr(
  318.             strtr(
  319.                 base64_encode(random_bytes($length)),
  320.                 '+/',
  321.                 '-_'
  322.             ),
  323.             0,
  324.             $length
  325.         );
  326.     }
  327.     /**
  328.      * Returns the default scopes used by this provider.
  329.      *
  330.      * This should only be the scopes that are required to request the details
  331.      * of the resource owner, rather than all the available scopes.
  332.      *
  333.      * @return array
  334.      */
  335.     abstract protected function getDefaultScopes();
  336.     /**
  337.      * Returns the string that should be used to separate scopes when building
  338.      * the URL for requesting an access token.
  339.      *
  340.      * @return string Scope separator, defaults to ','
  341.      */
  342.     protected function getScopeSeparator()
  343.     {
  344.         return ',';
  345.     }
  346.     /**
  347.      * @return string|null
  348.      */
  349.     protected function getPkceMethod()
  350.     {
  351.         return null;
  352.     }
  353.     /**
  354.      * Returns authorization parameters based on provided options.
  355.      *
  356.      * @param  array $options
  357.      * @return array Authorization parameters
  358.      */
  359.     protected function getAuthorizationParameters(array $options)
  360.     {
  361.         if (empty($options['state'])) {
  362.             $options['state'] = $this->getRandomState();
  363.         }
  364.         if (empty($options['scope'])) {
  365.             $options['scope'] = $this->getDefaultScopes();
  366.         }
  367.         $options += [
  368.             'response_type'   => 'code',
  369.             'approval_prompt' => 'auto'
  370.         ];
  371.         if (is_array($options['scope'])) {
  372.             $separator $this->getScopeSeparator();
  373.             $options['scope'] = implode($separator$options['scope']);
  374.         }
  375.         // Store the state as it may need to be accessed later on.
  376.         $this->state $options['state'];
  377.         $pkceMethod $this->getPkceMethod();
  378.         if (!empty($pkceMethod)) {
  379.             $this->pkceCode $this->getRandomPkceCode();
  380.             if ($pkceMethod === static::PKCE_METHOD_S256) {
  381.                 $options['code_challenge'] = trim(
  382.                     strtr(
  383.                         base64_encode(hash('sha256'$this->pkceCodetrue)),
  384.                         '+/',
  385.                         '-_'
  386.                     ),
  387.                     '='
  388.                 );
  389.             } elseif ($pkceMethod === static::PKCE_METHOD_PLAIN) {
  390.                 $options['code_challenge'] = $this->pkceCode;
  391.             } else {
  392.                 throw new InvalidArgumentException('Unknown PKCE method "' $pkceMethod '".');
  393.             }
  394.             $options['code_challenge_method'] = $pkceMethod;
  395.         }
  396.         // Business code layer might set a different redirect_uri parameter
  397.         // depending on the context, leave it as-is
  398.         if (!isset($options['redirect_uri'])) {
  399.             $options['redirect_uri'] = $this->redirectUri;
  400.         }
  401.         $options['client_id'] = $this->clientId;
  402.         return $options;
  403.     }
  404.     /**
  405.      * Builds the authorization URL's query string.
  406.      *
  407.      * @param  array $params Query parameters
  408.      * @return string Query string
  409.      */
  410.     protected function getAuthorizationQuery(array $params)
  411.     {
  412.         return $this->buildQueryString($params);
  413.     }
  414.     /**
  415.      * Builds the authorization URL.
  416.      *
  417.      * @param  array $options
  418.      * @return string Authorization URL
  419.      */
  420.     public function getAuthorizationUrl(array $options = [])
  421.     {
  422.         $base   $this->getBaseAuthorizationUrl();
  423.         $params $this->getAuthorizationParameters($options);
  424.         $query  $this->getAuthorizationQuery($params);
  425.         return $this->appendQuery($base$query);
  426.     }
  427.     /**
  428.      * Redirects the client for authorization.
  429.      *
  430.      * @param  array $options
  431.      * @param  callable|null $redirectHandler
  432.      * @return mixed
  433.      */
  434.     public function authorize(
  435.         array $options = [],
  436.         callable $redirectHandler null
  437.     ) {
  438.         $url $this->getAuthorizationUrl($options);
  439.         if ($redirectHandler) {
  440.             return $redirectHandler($url$this);
  441.         }
  442.         // @codeCoverageIgnoreStart
  443.         header('Location: ' $url);
  444.         exit;
  445.         // @codeCoverageIgnoreEnd
  446.     }
  447.     /**
  448.      * Appends a query string to a URL.
  449.      *
  450.      * @param  string $url The URL to append the query to
  451.      * @param  string $query The HTTP query string
  452.      * @return string The resulting URL
  453.      */
  454.     protected function appendQuery($url$query)
  455.     {
  456.         $query trim($query'?&');
  457.         if ($query) {
  458.             $glue strstr($url'?') === false '?' '&';
  459.             return $url $glue $query;
  460.         }
  461.         return $url;
  462.     }
  463.     /**
  464.      * Returns the method to use when requesting an access token.
  465.      *
  466.      * @return string HTTP method
  467.      */
  468.     protected function getAccessTokenMethod()
  469.     {
  470.         return self::METHOD_POST;
  471.     }
  472.     /**
  473.      * Returns the key used in the access token response to identify the resource owner.
  474.      *
  475.      * @return string|null Resource owner identifier key
  476.      */
  477.     protected function getAccessTokenResourceOwnerId()
  478.     {
  479.         return static::ACCESS_TOKEN_RESOURCE_OWNER_ID;
  480.     }
  481.     /**
  482.      * Builds the access token URL's query string.
  483.      *
  484.      * @param  array $params Query parameters
  485.      * @return string Query string
  486.      */
  487.     protected function getAccessTokenQuery(array $params)
  488.     {
  489.         return $this->buildQueryString($params);
  490.     }
  491.     /**
  492.      * Checks that a provided grant is valid, or attempts to produce one if the
  493.      * provided grant is a string.
  494.      *
  495.      * @param  AbstractGrant|string $grant
  496.      * @return AbstractGrant
  497.      */
  498.     protected function verifyGrant($grant)
  499.     {
  500.         if (is_string($grant)) {
  501.             return $this->grantFactory->getGrant($grant);
  502.         }
  503.         $this->grantFactory->checkGrant($grant);
  504.         return $grant;
  505.     }
  506.     /**
  507.      * Returns the full URL to use when requesting an access token.
  508.      *
  509.      * @param array $params Query parameters
  510.      * @return string
  511.      */
  512.     protected function getAccessTokenUrl(array $params)
  513.     {
  514.         $url $this->getBaseAccessTokenUrl($params);
  515.         if ($this->getAccessTokenMethod() === self::METHOD_GET) {
  516.             $query $this->getAccessTokenQuery($params);
  517.             return $this->appendQuery($url$query);
  518.         }
  519.         return $url;
  520.     }
  521.     /**
  522.      * Returns a prepared request for requesting an access token.
  523.      *
  524.      * @param array $params Query string parameters
  525.      * @return RequestInterface
  526.      */
  527.     protected function getAccessTokenRequest(array $params)
  528.     {
  529.         $method  $this->getAccessTokenMethod();
  530.         $url     $this->getAccessTokenUrl($params);
  531.         $options $this->optionProvider->getAccessTokenOptions($this->getAccessTokenMethod(), $params);
  532.         return $this->getRequest($method$url$options);
  533.     }
  534.     /**
  535.      * Requests an access token using a specified grant and option set.
  536.      *
  537.      * @param  mixed                $grant
  538.      * @param  array<string, mixed> $options
  539.      * @throws IdentityProviderException
  540.      * @return AccessTokenInterface
  541.      */
  542.     public function getAccessToken($grant, array $options = [])
  543.     {
  544.         $grant $this->verifyGrant($grant);
  545.         $params = [
  546.             'client_id'     => $this->clientId,
  547.             'client_secret' => $this->clientSecret,
  548.             'redirect_uri'  => $this->redirectUri,
  549.         ];
  550.         if (!empty($this->pkceCode)) {
  551.             $params['code_verifier'] = $this->pkceCode;
  552.         }
  553.         $params   $grant->prepareRequestParameters($params$options);
  554.         $request  $this->getAccessTokenRequest($params);
  555.         $response $this->getParsedResponse($request);
  556.         if (false === is_array($response)) {
  557.             throw new UnexpectedValueException(
  558.                 'Invalid response received from Authorization Server. Expected JSON.'
  559.             );
  560.         }
  561.         $prepared $this->prepareAccessTokenResponse($response);
  562.         $token    $this->createAccessToken($prepared$grant);
  563.         return $token;
  564.     }
  565.     /**
  566.      * Returns a PSR-7 request instance that is not authenticated.
  567.      *
  568.      * @param  string $method
  569.      * @param  string $url
  570.      * @param  array $options
  571.      * @return RequestInterface
  572.      */
  573.     public function getRequest($method$url, array $options = [])
  574.     {
  575.         return $this->createRequest($method$urlnull$options);
  576.     }
  577.     /**
  578.      * Returns an authenticated PSR-7 request instance.
  579.      *
  580.      * @param  string $method
  581.      * @param  string $url
  582.      * @param  AccessTokenInterface|string|null $token
  583.      * @param  array $options Any of "headers", "body", and "protocolVersion".
  584.      * @return RequestInterface
  585.      */
  586.     public function getAuthenticatedRequest($method$url$token, array $options = [])
  587.     {
  588.         return $this->createRequest($method$url$token$options);
  589.     }
  590.     /**
  591.      * Creates a PSR-7 request instance.
  592.      *
  593.      * @param  string $method
  594.      * @param  string $url
  595.      * @param  AccessTokenInterface|string|null $token
  596.      * @param  array $options
  597.      * @return RequestInterface
  598.      */
  599.     protected function createRequest($method$url$token, array $options)
  600.     {
  601.         $defaults = [
  602.             'headers' => $this->getHeaders($token),
  603.         ];
  604.         $options array_merge_recursive($defaults$options);
  605.         $factory $this->getRequestFactory();
  606.         return $factory->getRequestWithOptions($method$url$options);
  607.     }
  608.     /**
  609.      * Sends a request instance and returns a response instance.
  610.      *
  611.      * WARNING: This method does not attempt to catch exceptions caused by HTTP
  612.      * errors! It is recommended to wrap this method in a try/catch block.
  613.      *
  614.      * @param  RequestInterface $request
  615.      * @return ResponseInterface
  616.      */
  617.     public function getResponse(RequestInterface $request)
  618.     {
  619.         return $this->getHttpClient()->send($request);
  620.     }
  621.     /**
  622.      * Sends a request and returns the parsed response.
  623.      *
  624.      * @param  RequestInterface $request
  625.      * @throws IdentityProviderException
  626.      * @return mixed
  627.      */
  628.     public function getParsedResponse(RequestInterface $request)
  629.     {
  630.         try {
  631.             $response $this->getResponse($request);
  632.         } catch (BadResponseException $e) {
  633.             $response $e->getResponse();
  634.         }
  635.         $parsed $this->parseResponse($response);
  636.         $this->checkResponse($response$parsed);
  637.         return $parsed;
  638.     }
  639.     /**
  640.      * Attempts to parse a JSON response.
  641.      *
  642.      * @param  string $content JSON content from response body
  643.      * @return array Parsed JSON data
  644.      * @throws UnexpectedValueException if the content could not be parsed
  645.      */
  646.     protected function parseJson($content)
  647.     {
  648.         $content json_decode($contenttrue);
  649.         if (json_last_error() !== JSON_ERROR_NONE) {
  650.             throw new UnexpectedValueException(sprintf(
  651.                 "Failed to parse JSON response: %s",
  652.                 json_last_error_msg()
  653.             ));
  654.         }
  655.         return $content;
  656.     }
  657.     /**
  658.      * Returns the content type header of a response.
  659.      *
  660.      * @param  ResponseInterface $response
  661.      * @return string Semi-colon separated join of content-type headers.
  662.      */
  663.     protected function getContentType(ResponseInterface $response)
  664.     {
  665.         return join(';', (array) $response->getHeader('content-type'));
  666.     }
  667.     /**
  668.      * Parses the response according to its content-type header.
  669.      *
  670.      * @throws UnexpectedValueException
  671.      * @param  ResponseInterface $response
  672.      * @return array
  673.      */
  674.     protected function parseResponse(ResponseInterface $response)
  675.     {
  676.         $content = (string) $response->getBody();
  677.         $type $this->getContentType($response);
  678.         if (strpos($type'urlencoded') !== false) {
  679.             parse_str($content$parsed);
  680.             return $parsed;
  681.         }
  682.         // Attempt to parse the string as JSON regardless of content type,
  683.         // since some providers use non-standard content types. Only throw an
  684.         // exception if the JSON could not be parsed when it was expected to.
  685.         try {
  686.             return $this->parseJson($content);
  687.         } catch (UnexpectedValueException $e) {
  688.             if (strpos($type'json') !== false) {
  689.                 throw $e;
  690.             }
  691.             if ($response->getStatusCode() == 500) {
  692.                 throw new UnexpectedValueException(
  693.                     'An OAuth server error was encountered that did not contain a JSON body',
  694.                     0,
  695.                     $e
  696.                 );
  697.             }
  698.             return $content;
  699.         }
  700.     }
  701.     /**
  702.      * Checks a provider response for errors.
  703.      *
  704.      * @throws IdentityProviderException
  705.      * @param  ResponseInterface $response
  706.      * @param  array|string $data Parsed response data
  707.      * @return void
  708.      */
  709.     abstract protected function checkResponse(ResponseInterface $response$data);
  710.     /**
  711.      * Prepares an parsed access token response for a grant.
  712.      *
  713.      * Custom mapping of expiration, etc should be done here. Always call the
  714.      * parent method when overloading this method.
  715.      *
  716.      * @param  mixed $result
  717.      * @return array
  718.      */
  719.     protected function prepareAccessTokenResponse(array $result)
  720.     {
  721.         if ($this->getAccessTokenResourceOwnerId() !== null) {
  722.             $result['resource_owner_id'] = $this->getValueByKey(
  723.                 $result,
  724.                 $this->getAccessTokenResourceOwnerId()
  725.             );
  726.         }
  727.         return $result;
  728.     }
  729.     /**
  730.      * Creates an access token from a response.
  731.      *
  732.      * The grant that was used to fetch the response can be used to provide
  733.      * additional context.
  734.      *
  735.      * @param  array $response
  736.      * @param  AbstractGrant $grant
  737.      * @return AccessTokenInterface
  738.      */
  739.     protected function createAccessToken(array $responseAbstractGrant $grant)
  740.     {
  741.         return new AccessToken($response);
  742.     }
  743.     /**
  744.      * Generates a resource owner object from a successful resource owner
  745.      * details request.
  746.      *
  747.      * @param  array $response
  748.      * @param  AccessToken $token
  749.      * @return ResourceOwnerInterface
  750.      */
  751.     abstract protected function createResourceOwner(array $responseAccessToken $token);
  752.     /**
  753.      * Requests and returns the resource owner of given access token.
  754.      *
  755.      * @param  AccessToken $token
  756.      * @return ResourceOwnerInterface
  757.      */
  758.     public function getResourceOwner(AccessToken $token)
  759.     {
  760.         $response $this->fetchResourceOwnerDetails($token);
  761.         return $this->createResourceOwner($response$token);
  762.     }
  763.     /**
  764.      * Requests resource owner details.
  765.      *
  766.      * @param  AccessToken $token
  767.      * @return mixed
  768.      */
  769.     protected function fetchResourceOwnerDetails(AccessToken $token)
  770.     {
  771.         $url $this->getResourceOwnerDetailsUrl($token);
  772.         $request $this->getAuthenticatedRequest(self::METHOD_GET$url$token);
  773.         $response $this->getParsedResponse($request);
  774.         if (false === is_array($response)) {
  775.             throw new UnexpectedValueException(
  776.                 'Invalid response received from Authorization Server. Expected JSON.'
  777.             );
  778.         }
  779.         return $response;
  780.     }
  781.     /**
  782.      * Returns the default headers used by this provider.
  783.      *
  784.      * Typically this is used to set 'Accept' or 'Content-Type' headers.
  785.      *
  786.      * @return array
  787.      */
  788.     protected function getDefaultHeaders()
  789.     {
  790.         return [];
  791.     }
  792.     /**
  793.      * Returns the authorization headers used by this provider.
  794.      *
  795.      * Typically this is "Bearer" or "MAC". For more information see:
  796.      * http://tools.ietf.org/html/rfc6749#section-7.1
  797.      *
  798.      * No default is provided, providers must overload this method to activate
  799.      * authorization headers.
  800.      *
  801.      * @param  mixed|null $token Either a string or an access token instance
  802.      * @return array
  803.      */
  804.     protected function getAuthorizationHeaders($token null)
  805.     {
  806.         return [];
  807.     }
  808.     /**
  809.      * Returns all headers used by this provider for a request.
  810.      *
  811.      * The request will be authenticated if an access token is provided.
  812.      *
  813.      * @param  mixed|null $token object or string
  814.      * @return array
  815.      */
  816.     public function getHeaders($token null)
  817.     {
  818.         if ($token) {
  819.             return array_merge(
  820.                 $this->getDefaultHeaders(),
  821.                 $this->getAuthorizationHeaders($token)
  822.             );
  823.         }
  824.         return $this->getDefaultHeaders();
  825.     }
  826. }