PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / trunk
Yoast SEO – Advanced SEO with real-time guidance and built-in AI vtrunk
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / vendor_prefixed / league / oauth2-client / src / Provider / AbstractProvider.php

AbstractProvider.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI trunk, at vendor_prefixed/league/oauth2-client/src/Provider/AbstractProvider.php

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