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

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 <hello@alexbilbie.com>
  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($options, array_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->pkceCode, true)),
  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, $url, null, $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($content, true);
  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 $response, AbstractGrant $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 $response, AccessToken $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. }