source: vendor/google/apiclient/src/Client.php@ f9c482b

Last change on this file since f9c482b was f9c482b, checked in by Vlado 222039 <vlado.popovski@…>, 7 days ago

Upload new project files

  • Property mode set to 100644
File size: 44.2 KB
RevLine 
[f9c482b]1<?php
2/*
3 * Copyright 2010 Google Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18namespace Google;
19
20use BadMethodCallException;
21use DomainException;
22use Google\AccessToken\Revoke;
23use Google\AccessToken\Verify;
24use Google\Auth\ApplicationDefaultCredentials;
25use Google\Auth\Cache\MemoryCacheItemPool;
26use Google\Auth\Credentials\ServiceAccountCredentials;
27use Google\Auth\Credentials\UserRefreshCredentials;
28use Google\Auth\CredentialsLoader;
29use Google\Auth\FetchAuthTokenCache;
30use Google\Auth\GetUniverseDomainInterface;
31use Google\Auth\HttpHandler\HttpHandlerFactory;
32use Google\Auth\OAuth2;
33use Google\AuthHandler\AuthHandlerFactory;
34use Google\Http\REST;
35use GuzzleHttp\Client as GuzzleClient;
36use GuzzleHttp\ClientInterface;
37use GuzzleHttp\Ring\Client\StreamHandler;
38use InvalidArgumentException;
39use LogicException;
40use Monolog\Handler\StreamHandler as MonologStreamHandler;
41use Monolog\Handler\SyslogHandler as MonologSyslogHandler;
42use Monolog\Logger;
43use Psr\Cache\CacheItemPoolInterface;
44use Psr\Http\Message\RequestInterface;
45use Psr\Http\Message\ResponseInterface;
46use Psr\Log\LoggerInterface;
47use UnexpectedValueException;
48
49/**
50 * The Google API Client
51 * https://github.com/google/google-api-php-client
52 */
53class Client
54{
55 const LIBVER = "2.12.6";
56 const USER_AGENT_SUFFIX = "google-api-php-client/";
57 const OAUTH2_REVOKE_URI = 'https://oauth2.googleapis.com/revoke';
58 const OAUTH2_TOKEN_URI = 'https://oauth2.googleapis.com/token';
59 const OAUTH2_AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
60 const API_BASE_PATH = 'https://www.googleapis.com';
61
62 /**
63 * @var ?OAuth2 $auth
64 */
65 private $auth;
66
67 /**
68 * @var ClientInterface $http
69 */
70 private $http;
71
72 /**
73 * @var ?CacheItemPoolInterface $cache
74 */
75 private $cache;
76
77 /**
78 * @var array access token
79 */
80 private $token;
81
82 /**
83 * @var array $config
84 */
85 private $config;
86
87 /**
88 * @var ?LoggerInterface $logger
89 */
90 private $logger;
91
92 /**
93 * @var ?CredentialsLoader $credentials
94 */
95 private $credentials;
96
97 /**
98 * @var boolean $deferExecution
99 */
100 private $deferExecution = false;
101
102 /** @var array $scopes */
103 // Scopes requested by the client
104 protected $requestedScopes = [];
105
106 /**
107 * Construct the Google Client.
108 *
109 * @param array $config {
110 * An array of required and optional arguments.
111 *
112 * @type string $application_name
113 * The name of your application
114 * @type string $base_path
115 * The base URL for the service. This is only accounted for when calling
116 * {@see Client::authorize()} directly.
117 * @type string $client_id
118 * Your Google Cloud client ID found in https://developers.google.com/console
119 * @type string $client_secret
120 * Your Google Cloud client secret found in https://developers.google.com/console
121 * @type string|array|CredentialsLoader $credentials
122 * Can be a path to JSON credentials or an array representing those
123 * credentials (@see Google\Client::setAuthConfig), or an instance of
124 * {@see CredentialsLoader}.
125 * @type string|array $scopes
126 * {@see Google\Client::setScopes}
127 * @type string $quota_project
128 * Sets X-Goog-User-Project, which specifies a user project to bill
129 * for access charges associated with the request.
130 * @type string $redirect_uri
131 * @type string $state
132 * @type string $developer_key
133 * Simple API access key, also from the API console. Ensure you get
134 * a Server key, and not a Browser key.
135 * **NOTE:** The universe domain is assumed to be "googleapis.com" unless
136 * explicitly set. When setting an API ley directly via this option, there
137 * is no way to verify the universe domain. Be sure to set the
138 * "universe_domain" option if "googleapis.com" is not intended.
139 * @type bool $use_application_default_credentials
140 * For use with Google Cloud Platform
141 * fetch the ApplicationDefaultCredentials, if applicable
142 * {@see https://developers.google.com/identity/protocols/application-default-credentials}
143 * @type string $signing_key
144 * @type string $signing_algorithm
145 * @type string $subject
146 * @type string $hd
147 * @type string $prompt
148 * @type string $openid
149 * @type bool $include_granted_scopes
150 * @type string $login_hint
151 * @type string $request_visible_actions
152 * @type string $access_type
153 * @type string $approval_prompt
154 * @type array $retry
155 * Task Runner retry configuration
156 * {@see \Google\Task\Runner}
157 * @type array $retry_map
158 * @type CacheItemPoolInterface $cache
159 * Cache class implementing {@see CacheItemPoolInterface}. Defaults
160 * to {@see MemoryCacheItemPool}.
161 * @type array $cache_config
162 * Cache config for downstream auth caching.
163 * @type callable $token_callback
164 * Function to be called when an access token is fetched. Follows
165 * the signature `function (string $cacheKey, string $accessToken)`.
166 * @type \Firebase\JWT $jwt
167 * Service class used in {@see Client::verifyIdToken()}. Explicitly
168 * pass this in to avoid setting {@see \Firebase\JWT::$leeway}
169 * @type bool $api_format_v2
170 * Setting api_format_v2 will return more detailed error messages
171 * from certain APIs.
172 * @type string $universe_domain
173 * Setting the universe domain will change the default rootUrl of the service.
174 * If not set explicitly, the universe domain will be the value provided in the
175 *. "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable, or "googleapis.com".
176 * }
177 */
178 public function __construct(array $config = [])
179 {
180 $this->config = array_merge([
181 'application_name' => '',
182 'base_path' => self::API_BASE_PATH,
183 'client_id' => '',
184 'client_secret' => '',
185 'credentials' => null,
186 'scopes' => null,
187 'quota_project' => null,
188 'redirect_uri' => null,
189 'state' => null,
190 'developer_key' => '',
191 'use_application_default_credentials' => false,
192 'signing_key' => null,
193 'signing_algorithm' => null,
194 'subject' => null,
195 'hd' => '',
196 'prompt' => '',
197 'openid.realm' => '',
198 'include_granted_scopes' => null,
199 'logger' => null,
200 'login_hint' => '',
201 'request_visible_actions' => '',
202 'access_type' => 'online',
203 'approval_prompt' => 'auto',
204 'retry' => [],
205 'retry_map' => null,
206 'cache' => null,
207 'cache_config' => [],
208 'token_callback' => null,
209 'jwt' => null,
210 'api_format_v2' => false,
211 'universe_domain' => getenv('GOOGLE_CLOUD_UNIVERSE_DOMAIN')
212 ?: GetUniverseDomainInterface::DEFAULT_UNIVERSE_DOMAIN,
213 ], $config);
214
215 if (!is_null($this->config['credentials'])) {
216 if ($this->config['credentials'] instanceof CredentialsLoader) {
217 $this->credentials = $this->config['credentials'];
218 } else {
219 $this->setAuthConfig($this->config['credentials']);
220 }
221 unset($this->config['credentials']);
222 }
223
224 if (!is_null($this->config['scopes'])) {
225 $this->setScopes($this->config['scopes']);
226 unset($this->config['scopes']);
227 }
228
229 // Set a default token callback to update the in-memory access token
230 if (is_null($this->config['token_callback'])) {
231 $this->config['token_callback'] = function ($cacheKey, $newAccessToken) {
232 $this->setAccessToken(
233 [
234 'access_token' => $newAccessToken,
235 'expires_in' => 3600, // Google default
236 'created' => time(),
237 ]
238 );
239 };
240 }
241
242 if (!is_null($this->config['cache'])) {
243 $this->setCache($this->config['cache']);
244 unset($this->config['cache']);
245 }
246
247 if (!is_null($this->config['logger'])) {
248 $this->setLogger($this->config['logger']);
249 unset($this->config['logger']);
250 }
251 }
252
253 /**
254 * Get a string containing the version of the library.
255 *
256 * @return string
257 */
258 public function getLibraryVersion()
259 {
260 return self::LIBVER;
261 }
262
263 /**
264 * For backwards compatibility
265 * alias for fetchAccessTokenWithAuthCode
266 *
267 * @param string $code string code from accounts.google.com
268 * @return array access token
269 * @deprecated
270 */
271 public function authenticate($code)
272 {
273 return $this->fetchAccessTokenWithAuthCode($code);
274 }
275
276 /**
277 * Attempt to exchange a code for an valid authentication token.
278 * Helper wrapped around the OAuth 2.0 implementation.
279 *
280 * @param string $code code from accounts.google.com
281 * @param string $codeVerifier the code verifier used for PKCE (if applicable)
282 * @return array access token
283 */
284 public function fetchAccessTokenWithAuthCode($code, $codeVerifier = null)
285 {
286 if (strlen($code) == 0) {
287 throw new InvalidArgumentException("Invalid code");
288 }
289
290 $auth = $this->getOAuth2Service();
291 $auth->setCode($code);
292 $auth->setRedirectUri($this->getRedirectUri());
293 if ($codeVerifier) {
294 $auth->setCodeVerifier($codeVerifier);
295 }
296
297 $httpHandler = HttpHandlerFactory::build($this->getHttpClient());
298 $creds = $auth->fetchAuthToken($httpHandler);
299 if ($creds && isset($creds['access_token'])) {
300 $creds['created'] = time();
301 $this->setAccessToken($creds);
302 }
303
304 return $creds;
305 }
306
307 /**
308 * For backwards compatibility
309 * alias for fetchAccessTokenWithAssertion
310 *
311 * @return array access token
312 * @deprecated
313 */
314 public function refreshTokenWithAssertion()
315 {
316 return $this->fetchAccessTokenWithAssertion();
317 }
318
319 /**
320 * Fetches a fresh access token with a given assertion token.
321 * @param ClientInterface $authHttp optional.
322 * @return array access token
323 */
324 public function fetchAccessTokenWithAssertion(?ClientInterface $authHttp = null)
325 {
326 if (!$this->isUsingApplicationDefaultCredentials()) {
327 throw new DomainException(
328 'set the JSON service account credentials using'
329 . ' Google\Client::setAuthConfig or set the path to your JSON file'
330 . ' with the "GOOGLE_APPLICATION_CREDENTIALS" environment variable'
331 . ' and call Google\Client::useApplicationDefaultCredentials to'
332 . ' refresh a token with assertion.'
333 );
334 }
335
336 $this->getLogger()->log(
337 'info',
338 'OAuth2 access token refresh with Signed JWT assertion grants.'
339 );
340
341 $credentials = $this->createApplicationDefaultCredentials();
342
343 $httpHandler = HttpHandlerFactory::build($authHttp);
344 $creds = $credentials->fetchAuthToken($httpHandler);
345 if ($creds && isset($creds['access_token'])) {
346 $creds['created'] = time();
347 $this->setAccessToken($creds);
348 }
349
350 return $creds;
351 }
352
353 /**
354 * For backwards compatibility
355 * alias for fetchAccessTokenWithRefreshToken
356 *
357 * @param string $refreshToken
358 * @return array access token
359 */
360 public function refreshToken($refreshToken)
361 {
362 return $this->fetchAccessTokenWithRefreshToken($refreshToken);
363 }
364
365 /**
366 * Fetches a fresh OAuth 2.0 access token with the given refresh token.
367 * @param string $refreshToken
368 * @return array access token
369 */
370 public function fetchAccessTokenWithRefreshToken($refreshToken = null)
371 {
372 if (null === $refreshToken) {
373 if (!isset($this->token['refresh_token'])) {
374 throw new LogicException(
375 'refresh token must be passed in or set as part of setAccessToken'
376 );
377 }
378 $refreshToken = $this->token['refresh_token'];
379 }
380 $this->getLogger()->info('OAuth2 access token refresh');
381 $auth = $this->getOAuth2Service();
382 $auth->setRefreshToken($refreshToken);
383
384 $httpHandler = HttpHandlerFactory::build($this->getHttpClient());
385 $creds = $auth->fetchAuthToken($httpHandler);
386 if ($creds && isset($creds['access_token'])) {
387 $creds['created'] = time();
388 if (!isset($creds['refresh_token'])) {
389 $creds['refresh_token'] = $refreshToken;
390 }
391 $this->setAccessToken($creds);
392 }
393
394 return $creds;
395 }
396
397 /**
398 * Create a URL to obtain user authorization.
399 * The authorization endpoint allows the user to first
400 * authenticate, and then grant/deny the access request.
401 * @param string|array $scope The scope is expressed as an array or list of space-delimited strings.
402 * @param array $queryParams Querystring params to add to the authorization URL.
403 * @return string
404 */
405 public function createAuthUrl($scope = null, array $queryParams = [])
406 {
407 if (empty($scope)) {
408 $scope = $this->prepareScopes();
409 }
410 if (is_array($scope)) {
411 $scope = implode(' ', $scope);
412 }
413
414 // only accept one of prompt or approval_prompt
415 $approvalPrompt = $this->config['prompt']
416 ? null
417 : $this->config['approval_prompt'];
418
419 // include_granted_scopes should be string "true", string "false", or null
420 $includeGrantedScopes = $this->config['include_granted_scopes'] === null
421 ? null
422 : var_export($this->config['include_granted_scopes'], true);
423
424 $params = array_filter([
425 'access_type' => $this->config['access_type'],
426 'approval_prompt' => $approvalPrompt,
427 'hd' => $this->config['hd'],
428 'include_granted_scopes' => $includeGrantedScopes,
429 'login_hint' => $this->config['login_hint'],
430 'openid.realm' => $this->config['openid.realm'],
431 'prompt' => $this->config['prompt'],
432 'redirect_uri' => $this->config['redirect_uri'],
433 'response_type' => 'code',
434 'scope' => $scope,
435 'state' => $this->config['state'],
436 ]) + $queryParams;
437
438 // If the list of scopes contains plus.login, add request_visible_actions
439 // to auth URL.
440 $rva = $this->config['request_visible_actions'];
441 if (strlen($rva) > 0 && false !== strpos($scope, 'plus.login')) {
442 $params['request_visible_actions'] = $rva;
443 }
444
445 $auth = $this->getOAuth2Service();
446
447 return (string) $auth->buildFullAuthorizationUri($params);
448 }
449
450 /**
451 * Adds auth listeners to the HTTP client based on the credentials
452 * set in the Google API Client object
453 *
454 * @param ClientInterface $http the http client object.
455 * @return ClientInterface the http client object
456 */
457 public function authorize(?ClientInterface $http = null)
458 {
459 $http = $http ?: $this->getHttpClient();
460 $authHandler = $this->getAuthHandler();
461
462 // These conditionals represent the decision tree for authentication
463 // 1. Check if a Google\Auth\CredentialsLoader instance has been supplied via the "credentials" option
464 // 2. Check for Application Default Credentials
465 // 3a. Check for an Access Token
466 // 3b. If access token exists but is expired, try to refresh it
467 // 4. Check for API Key
468 if ($this->credentials) {
469 $this->checkUniverseDomain($this->credentials);
470 return $authHandler->attachCredentials(
471 $http,
472 $this->credentials,
473 $this->config['token_callback']
474 );
475 }
476
477 if ($this->isUsingApplicationDefaultCredentials()) {
478 $credentials = $this->createApplicationDefaultCredentials();
479 $this->checkUniverseDomain($credentials);
480 return $authHandler->attachCredentialsCache(
481 $http,
482 $credentials,
483 $this->config['token_callback']
484 );
485 }
486
487 if ($token = $this->getAccessToken()) {
488 $scopes = $this->prepareScopes();
489 // add refresh subscriber to request a new token
490 if (isset($token['refresh_token']) && $this->isAccessTokenExpired()) {
491 $credentials = $this->createUserRefreshCredentials(
492 $scopes,
493 $token['refresh_token']
494 );
495 $this->checkUniverseDomain($credentials);
496 return $authHandler->attachCredentials(
497 $http,
498 $credentials,
499 $this->config['token_callback']
500 );
501 }
502
503 return $authHandler->attachToken($http, $token, (array) $scopes);
504 }
505
506 if ($key = $this->config['developer_key']) {
507 return $authHandler->attachKey($http, $key);
508 }
509
510 return $http;
511 }
512
513 /**
514 * Set the configuration to use application default credentials for
515 * authentication
516 *
517 * @see https://developers.google.com/identity/protocols/application-default-credentials
518 * @param boolean $useAppCreds
519 */
520 public function useApplicationDefaultCredentials($useAppCreds = true)
521 {
522 $this->config['use_application_default_credentials'] = $useAppCreds;
523 }
524
525 /**
526 * To prevent useApplicationDefaultCredentials from inappropriately being
527 * called in a conditional
528 *
529 * @see https://developers.google.com/identity/protocols/application-default-credentials
530 */
531 public function isUsingApplicationDefaultCredentials()
532 {
533 return $this->config['use_application_default_credentials'];
534 }
535
536 /**
537 * Set the access token used for requests.
538 *
539 * Note that at the time requests are sent, tokens are cached. A token will be
540 * cached for each combination of service and authentication scopes. If a
541 * cache pool is not provided, creating a new instance of the client will
542 * allow modification of access tokens. If a persistent cache pool is
543 * provided, in order to change the access token, you must clear the cached
544 * token by calling `$client->getCache()->clear()`. (Use caution in this case,
545 * as calling `clear()` will remove all cache items, including any items not
546 * related to Google API PHP Client.)
547 *
548 * **NOTE:** The universe domain is assumed to be "googleapis.com" unless
549 * explicitly set. When setting an access token directly via this method, there
550 * is no way to verify the universe domain. Be sure to set the "universe_domain"
551 * option if "googleapis.com" is not intended.
552 *
553 * @param string|array $token
554 * @throws InvalidArgumentException
555 */
556 public function setAccessToken($token)
557 {
558 if (is_string($token)) {
559 if ($json = json_decode($token, true)) {
560 $token = $json;
561 } else {
562 // assume $token is just the token string
563 $token = [
564 'access_token' => $token,
565 ];
566 }
567 }
568 if ($token == null) {
569 throw new InvalidArgumentException('invalid json token');
570 }
571 if (!isset($token['access_token'])) {
572 throw new InvalidArgumentException("Invalid token format");
573 }
574 $this->token = $token;
575 }
576
577 public function getAccessToken()
578 {
579 return $this->token;
580 }
581
582 /**
583 * @return string|null
584 */
585 public function getRefreshToken()
586 {
587 if (isset($this->token['refresh_token'])) {
588 return $this->token['refresh_token'];
589 }
590
591 return null;
592 }
593
594 /**
595 * Returns if the access_token is expired.
596 * @return bool Returns True if the access_token is expired.
597 */
598 public function isAccessTokenExpired()
599 {
600 if (!$this->token) {
601 return true;
602 }
603
604 $created = 0;
605 if (isset($this->token['created'])) {
606 $created = $this->token['created'];
607 } elseif (isset($this->token['id_token'])) {
608 // check the ID token for "iat"
609 // signature verification is not required here, as we are just
610 // using this for convenience to save a round trip request
611 // to the Google API server
612 $idToken = $this->token['id_token'];
613 if (substr_count($idToken, '.') == 2) {
614 $parts = explode('.', $idToken);
615 $payload = json_decode(base64_decode($parts[1]), true);
616 if ($payload && isset($payload['iat'])) {
617 $created = $payload['iat'];
618 }
619 }
620 }
621 if (!isset($this->token['expires_in'])) {
622 // if the token does not have an "expires_in", then it's considered expired
623 return true;
624 }
625
626 // If the token is set to expire in the next 30 seconds.
627 return ($created + ($this->token['expires_in'] - 30)) < time();
628 }
629
630 /**
631 * @deprecated See UPGRADING.md for more information
632 */
633 public function getAuth()
634 {
635 throw new BadMethodCallException(
636 'This function no longer exists. See UPGRADING.md for more information'
637 );
638 }
639
640 /**
641 * @deprecated See UPGRADING.md for more information
642 */
643 public function setAuth($auth)
644 {
645 throw new BadMethodCallException(
646 'This function no longer exists. See UPGRADING.md for more information'
647 );
648 }
649
650 /**
651 * Set the OAuth 2.0 Client ID.
652 * @param string $clientId
653 */
654 public function setClientId($clientId)
655 {
656 $this->config['client_id'] = $clientId;
657 }
658
659 public function getClientId()
660 {
661 return $this->config['client_id'];
662 }
663
664 /**
665 * Set the OAuth 2.0 Client Secret.
666 * @param string $clientSecret
667 */
668 public function setClientSecret($clientSecret)
669 {
670 $this->config['client_secret'] = $clientSecret;
671 }
672
673 public function getClientSecret()
674 {
675 return $this->config['client_secret'];
676 }
677
678 /**
679 * Set the OAuth 2.0 Redirect URI.
680 * @param string $redirectUri
681 */
682 public function setRedirectUri($redirectUri)
683 {
684 $this->config['redirect_uri'] = $redirectUri;
685 }
686
687 public function getRedirectUri()
688 {
689 return $this->config['redirect_uri'];
690 }
691
692 /**
693 * Set OAuth 2.0 "state" parameter to achieve per-request customization.
694 * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-3.1.2.2
695 * @param string $state
696 */
697 public function setState($state)
698 {
699 $this->config['state'] = $state;
700 }
701
702 /**
703 * @param string $accessType Possible values for access_type include:
704 * {@code "offline"} to request offline access from the user.
705 * {@code "online"} to request online access from the user.
706 */
707 public function setAccessType($accessType)
708 {
709 $this->config['access_type'] = $accessType;
710 }
711
712 /**
713 * @param string $approvalPrompt Possible values for approval_prompt include:
714 * {@code "force"} to force the approval UI to appear.
715 * {@code "auto"} to request auto-approval when possible. (This is the default value)
716 */
717 public function setApprovalPrompt($approvalPrompt)
718 {
719 $this->config['approval_prompt'] = $approvalPrompt;
720 }
721
722 /**
723 * Set the login hint, email address or sub id.
724 * @param string $loginHint
725 */
726 public function setLoginHint($loginHint)
727 {
728 $this->config['login_hint'] = $loginHint;
729 }
730
731 /**
732 * Set the application name, this is included in the User-Agent HTTP header.
733 * @param string $applicationName
734 */
735 public function setApplicationName($applicationName)
736 {
737 $this->config['application_name'] = $applicationName;
738 }
739
740 /**
741 * If 'plus.login' is included in the list of requested scopes, you can use
742 * this method to define types of app activities that your app will write.
743 * You can find a list of available types here:
744 * @link https://developers.google.com/+/api/moment-types
745 *
746 * @param array $requestVisibleActions Array of app activity types
747 */
748 public function setRequestVisibleActions($requestVisibleActions)
749 {
750 if (is_array($requestVisibleActions)) {
751 $requestVisibleActions = implode(" ", $requestVisibleActions);
752 }
753 $this->config['request_visible_actions'] = $requestVisibleActions;
754 }
755
756 /**
757 * Set the developer key to use, these are obtained through the API Console.
758 * @see http://code.google.com/apis/console-help/#generatingdevkeys
759 * @param string $developerKey
760 */
761 public function setDeveloperKey($developerKey)
762 {
763 $this->config['developer_key'] = $developerKey;
764 }
765
766 /**
767 * Set the hd (hosted domain) parameter streamlines the login process for
768 * Google Apps hosted accounts. By including the domain of the user, you
769 * restrict sign-in to accounts at that domain.
770 * @param string $hd the domain to use.
771 */
772 public function setHostedDomain($hd)
773 {
774 $this->config['hd'] = $hd;
775 }
776
777 /**
778 * Set the prompt hint. Valid values are none, consent and select_account.
779 * If no value is specified and the user has not previously authorized
780 * access, then the user is shown a consent screen.
781 * @param string $prompt
782 * {@code "none"} Do not display any authentication or consent screens. Must not be specified with other values.
783 * {@code "consent"} Prompt the user for consent.
784 * {@code "select_account"} Prompt the user to select an account.
785 */
786 public function setPrompt($prompt)
787 {
788 $this->config['prompt'] = $prompt;
789 }
790
791 /**
792 * openid.realm is a parameter from the OpenID 2.0 protocol, not from OAuth
793 * 2.0. It is used in OpenID 2.0 requests to signify the URL-space for which
794 * an authentication request is valid.
795 * @param string $realm the URL-space to use.
796 */
797 public function setOpenidRealm($realm)
798 {
799 $this->config['openid.realm'] = $realm;
800 }
801
802 /**
803 * If this is provided with the value true, and the authorization request is
804 * granted, the authorization will include any previous authorizations
805 * granted to this user/application combination for other scopes.
806 * @param bool $include the URL-space to use.
807 */
808 public function setIncludeGrantedScopes($include)
809 {
810 $this->config['include_granted_scopes'] = $include;
811 }
812
813 /**
814 * sets function to be called when an access token is fetched
815 * @param callable $tokenCallback - function ($cacheKey, $accessToken)
816 */
817 public function setTokenCallback(callable $tokenCallback)
818 {
819 $this->config['token_callback'] = $tokenCallback;
820 }
821
822 /**
823 * Revoke an OAuth2 access token or refresh token. This method will revoke the current access
824 * token, if a token isn't provided.
825 *
826 * @param string|array|null $token The token (access token or a refresh token) that should be revoked.
827 * @return boolean Returns True if the revocation was successful, otherwise False.
828 */
829 public function revokeToken($token = null)
830 {
831 $tokenRevoker = new Revoke($this->getHttpClient());
832
833 return $tokenRevoker->revokeToken($token ?: $this->getAccessToken());
834 }
835
836 /**
837 * Verify an id_token. This method will verify the current id_token, if one
838 * isn't provided.
839 *
840 * @throws LogicException If no token was provided and no token was set using `setAccessToken`.
841 * @throws UnexpectedValueException If the token is not a valid JWT.
842 * @param string|null $idToken The token (id_token) that should be verified.
843 * @return array|false Returns the token payload as an array if the verification was
844 * successful, false otherwise.
845 */
846 public function verifyIdToken($idToken = null)
847 {
848 $tokenVerifier = new Verify(
849 $this->getHttpClient(),
850 $this->getCache(),
851 $this->config['jwt']
852 );
853
854 if (null === $idToken) {
855 $token = $this->getAccessToken();
856 if (!isset($token['id_token'])) {
857 throw new LogicException(
858 'id_token must be passed in or set as part of setAccessToken'
859 );
860 }
861 $idToken = $token['id_token'];
862 }
863
864 return $tokenVerifier->verifyIdToken(
865 $idToken,
866 $this->getClientId()
867 );
868 }
869
870 /**
871 * Set the scopes to be requested. Must be called before createAuthUrl().
872 * Will remove any previously configured scopes.
873 * @param string|array $scope_or_scopes, ie:
874 * array(
875 * 'https://www.googleapis.com/auth/plus.login',
876 * 'https://www.googleapis.com/auth/moderator'
877 * );
878 */
879 public function setScopes($scope_or_scopes)
880 {
881 $this->requestedScopes = [];
882 $this->addScope($scope_or_scopes);
883 }
884
885 /**
886 * This functions adds a scope to be requested as part of the OAuth2.0 flow.
887 * Will append any scopes not previously requested to the scope parameter.
888 * A single string will be treated as a scope to request. An array of strings
889 * will each be appended.
890 * @param string|string[] $scope_or_scopes e.g. "profile"
891 */
892 public function addScope($scope_or_scopes)
893 {
894 if (is_string($scope_or_scopes) && !in_array($scope_or_scopes, $this->requestedScopes)) {
895 $this->requestedScopes[] = $scope_or_scopes;
896 } elseif (is_array($scope_or_scopes)) {
897 foreach ($scope_or_scopes as $scope) {
898 $this->addScope($scope);
899 }
900 }
901 }
902
903 /**
904 * Returns the list of scopes requested by the client
905 * @return array the list of scopes
906 *
907 */
908 public function getScopes()
909 {
910 return $this->requestedScopes;
911 }
912
913 /**
914 * @return string|null
915 * @visible For Testing
916 */
917 public function prepareScopes()
918 {
919 if (empty($this->requestedScopes)) {
920 return null;
921 }
922
923 return implode(' ', $this->requestedScopes);
924 }
925
926 /**
927 * Helper method to execute deferred HTTP requests.
928 *
929 * @template T
930 * @param RequestInterface $request
931 * @param class-string<T>|false|null $expectedClass
932 * @throws \Google\Exception
933 * @return mixed|T|ResponseInterface
934 */
935 public function execute(RequestInterface $request, $expectedClass = null)
936 {
937 $request = $request
938 ->withHeader(
939 'User-Agent',
940 sprintf(
941 '%s %s%s',
942 $this->config['application_name'],
943 self::USER_AGENT_SUFFIX,
944 $this->getLibraryVersion()
945 )
946 )
947 ->withHeader(
948 'x-goog-api-client',
949 sprintf(
950 'gl-php/%s gdcl/%s',
951 phpversion(),
952 $this->getLibraryVersion()
953 )
954 );
955
956 if ($this->config['api_format_v2']) {
957 $request = $request->withHeader(
958 'X-GOOG-API-FORMAT-VERSION',
959 '2'
960 );
961 }
962
963 // call the authorize method
964 // this is where most of the grunt work is done
965 $http = $this->authorize();
966
967 return REST::execute(
968 $http,
969 $request,
970 $expectedClass,
971 $this->config['retry'],
972 $this->config['retry_map']
973 );
974 }
975
976 /**
977 * Declare whether batch calls should be used. This may increase throughput
978 * by making multiple requests in one connection.
979 *
980 * @param boolean $useBatch True if the batch support should
981 * be enabled. Defaults to False.
982 */
983 public function setUseBatch($useBatch)
984 {
985 // This is actually an alias for setDefer.
986 $this->setDefer($useBatch);
987 }
988
989 /**
990 * Are we running in Google AppEngine?
991 * return bool
992 */
993 public function isAppEngine()
994 {
995 return (isset($_SERVER['SERVER_SOFTWARE']) &&
996 strpos($_SERVER['SERVER_SOFTWARE'], 'Google App Engine') !== false);
997 }
998
999 public function setConfig($name, $value)
1000 {
1001 $this->config[$name] = $value;
1002 }
1003
1004 public function getConfig($name, $default = null)
1005 {
1006 return isset($this->config[$name]) ? $this->config[$name] : $default;
1007 }
1008
1009 /**
1010 * For backwards compatibility
1011 * alias for setAuthConfig
1012 *
1013 * @param string $file the configuration file
1014 * @throws \Google\Exception
1015 * @deprecated
1016 */
1017 public function setAuthConfigFile($file)
1018 {
1019 $this->setAuthConfig($file);
1020 }
1021
1022 /**
1023 * Set the auth config from new or deprecated JSON config.
1024 * This structure should match the file downloaded from
1025 * the "Download JSON" button on in the Google Developer
1026 * Console.
1027 * @param string|array $config the configuration json
1028 * @throws \Google\Exception
1029 */
1030 public function setAuthConfig($config)
1031 {
1032 if (is_string($config)) {
1033 if (!file_exists($config)) {
1034 throw new InvalidArgumentException(sprintf('file "%s" does not exist', $config));
1035 }
1036
1037 $json = file_get_contents($config);
1038
1039 if (!$config = json_decode($json, true)) {
1040 throw new LogicException('invalid json for auth config');
1041 }
1042 }
1043
1044 $key = isset($config['installed']) ? 'installed' : 'web';
1045 if (isset($config['type']) && $config['type'] == 'service_account') {
1046 // @TODO(v3): Remove this, as it isn't accurate. ADC applies only to determining
1047 // credentials based on the user's environment.
1048 $this->useApplicationDefaultCredentials();
1049
1050 // set the information from the config
1051 $this->setClientId($config['client_id']);
1052 $this->config['client_email'] = $config['client_email'];
1053 $this->config['signing_key'] = $config['private_key'];
1054 $this->config['signing_algorithm'] = 'HS256';
1055 } elseif (isset($config[$key])) {
1056 // old-style
1057 $this->setClientId($config[$key]['client_id']);
1058 $this->setClientSecret($config[$key]['client_secret']);
1059 if (isset($config[$key]['redirect_uris'])) {
1060 $this->setRedirectUri($config[$key]['redirect_uris'][0]);
1061 }
1062 } else {
1063 // new-style
1064 $this->setClientId($config['client_id']);
1065 $this->setClientSecret($config['client_secret']);
1066 if (isset($config['redirect_uris'])) {
1067 $this->setRedirectUri($config['redirect_uris'][0]);
1068 }
1069 }
1070 }
1071
1072 /**
1073 * Use when the service account has been delegated domain wide access.
1074 *
1075 * @param string $subject an email address account to impersonate
1076 */
1077 public function setSubject($subject)
1078 {
1079 $this->config['subject'] = $subject;
1080 }
1081
1082 /**
1083 * Declare whether making API calls should make the call immediately, or
1084 * return a request which can be called with ->execute();
1085 *
1086 * @param boolean $defer True if calls should not be executed right away.
1087 */
1088 public function setDefer($defer)
1089 {
1090 $this->deferExecution = $defer;
1091 }
1092
1093 /**
1094 * Whether or not to return raw requests
1095 * @return boolean
1096 */
1097 public function shouldDefer()
1098 {
1099 return $this->deferExecution;
1100 }
1101
1102 /**
1103 * @return OAuth2 implementation
1104 */
1105 public function getOAuth2Service()
1106 {
1107 if (!isset($this->auth)) {
1108 $this->auth = $this->createOAuth2Service();
1109 }
1110
1111 return $this->auth;
1112 }
1113
1114 /**
1115 * create a default google auth object
1116 */
1117 protected function createOAuth2Service()
1118 {
1119 $auth = new OAuth2([
1120 'clientId' => $this->getClientId(),
1121 'clientSecret' => $this->getClientSecret(),
1122 'authorizationUri' => self::OAUTH2_AUTH_URL,
1123 'tokenCredentialUri' => self::OAUTH2_TOKEN_URI,
1124 'redirectUri' => $this->getRedirectUri(),
1125 'issuer' => $this->config['client_id'],
1126 'signingKey' => $this->config['signing_key'],
1127 'signingAlgorithm' => $this->config['signing_algorithm'],
1128 ]);
1129
1130 return $auth;
1131 }
1132
1133 /**
1134 * Set the Cache object
1135 * @param CacheItemPoolInterface $cache
1136 */
1137 public function setCache(CacheItemPoolInterface $cache)
1138 {
1139 $this->cache = $cache;
1140 }
1141
1142 /**
1143 * @return CacheItemPoolInterface
1144 */
1145 public function getCache()
1146 {
1147 if (!$this->cache) {
1148 $this->cache = $this->createDefaultCache();
1149 }
1150
1151 return $this->cache;
1152 }
1153
1154 /**
1155 * @param array $cacheConfig
1156 */
1157 public function setCacheConfig(array $cacheConfig)
1158 {
1159 $this->config['cache_config'] = $cacheConfig;
1160 }
1161
1162 /**
1163 * Set the Logger object
1164 * @param LoggerInterface $logger
1165 */
1166 public function setLogger(LoggerInterface $logger)
1167 {
1168 $this->logger = $logger;
1169 }
1170
1171 /**
1172 * @return LoggerInterface
1173 */
1174 public function getLogger()
1175 {
1176 if (!isset($this->logger)) {
1177 $this->logger = $this->createDefaultLogger();
1178 }
1179
1180 return $this->logger;
1181 }
1182
1183 protected function createDefaultLogger()
1184 {
1185 $logger = new Logger('google-api-php-client');
1186 if ($this->isAppEngine()) {
1187 $handler = new MonologSyslogHandler('app', LOG_USER, Logger::NOTICE);
1188 } else {
1189 $handler = new MonologStreamHandler('php://stderr', Logger::NOTICE);
1190 }
1191 $logger->pushHandler($handler);
1192
1193 return $logger;
1194 }
1195
1196 protected function createDefaultCache()
1197 {
1198 return new MemoryCacheItemPool();
1199 }
1200
1201 /**
1202 * Set the Http Client object
1203 * @param ClientInterface $http
1204 */
1205 public function setHttpClient(ClientInterface $http)
1206 {
1207 $this->http = $http;
1208 }
1209
1210 /**
1211 * @return ClientInterface
1212 */
1213 public function getHttpClient()
1214 {
1215 if (null === $this->http) {
1216 $this->http = $this->createDefaultHttpClient();
1217 }
1218
1219 return $this->http;
1220 }
1221
1222 /**
1223 * Set the API format version.
1224 *
1225 * `true` will use V2, which may return more useful error messages.
1226 *
1227 * @param bool $value
1228 */
1229 public function setApiFormatV2($value)
1230 {
1231 $this->config['api_format_v2'] = (bool) $value;
1232 }
1233
1234 protected function createDefaultHttpClient()
1235 {
1236 $guzzleVersion = null;
1237 if (defined('\GuzzleHttp\ClientInterface::MAJOR_VERSION')) {
1238 $guzzleVersion = ClientInterface::MAJOR_VERSION;
1239 } elseif (defined('\GuzzleHttp\ClientInterface::VERSION')) {
1240 $guzzleVersion = (int)substr(ClientInterface::VERSION, 0, 1);
1241 }
1242
1243 if (5 === $guzzleVersion) {
1244 $options = [
1245 'base_url' => $this->config['base_path'],
1246 'defaults' => ['exceptions' => false],
1247 ];
1248 if ($this->isAppEngine()) {
1249 if (class_exists(StreamHandler::class)) {
1250 // set StreamHandler on AppEngine by default
1251 $options['handler'] = new StreamHandler();
1252 $options['defaults']['verify'] = '/etc/ca-certificates.crt';
1253 }
1254 }
1255 } elseif (6 === $guzzleVersion || 7 === $guzzleVersion) {
1256 // guzzle 6 or 7
1257 $options = [
1258 'base_uri' => $this->config['base_path'],
1259 'http_errors' => false,
1260 ];
1261 } else {
1262 throw new LogicException('Could not find supported version of Guzzle.');
1263 }
1264
1265 return new GuzzleClient($options);
1266 }
1267
1268 /**
1269 * @return FetchAuthTokenCache
1270 */
1271 private function createApplicationDefaultCredentials()
1272 {
1273 $scopes = $this->prepareScopes();
1274 $sub = $this->config['subject'];
1275 $signingKey = $this->config['signing_key'];
1276
1277 // create credentials using values supplied in setAuthConfig
1278 if ($signingKey) {
1279 $serviceAccountCredentials = [
1280 'client_id' => $this->config['client_id'],
1281 'client_email' => $this->config['client_email'],
1282 'private_key' => $signingKey,
1283 'type' => 'service_account',
1284 'quota_project_id' => $this->config['quota_project'],
1285 ];
1286 $credentials = CredentialsLoader::makeCredentials(
1287 $scopes,
1288 $serviceAccountCredentials
1289 );
1290 } else {
1291 // When $sub is provided, we cannot pass cache classes to ::getCredentials
1292 // because FetchAuthTokenCache::setSub does not exist.
1293 // The result is when $sub is provided, calls to ::onGce are not cached.
1294 $credentials = ApplicationDefaultCredentials::getCredentials(
1295 $scopes,
1296 null,
1297 $sub ? null : $this->config['cache_config'],
1298 $sub ? null : $this->getCache(),
1299 $this->config['quota_project']
1300 );
1301 }
1302
1303 // for service account domain-wide authority (impersonating a user)
1304 // @see https://developers.google.com/identity/protocols/OAuth2ServiceAccount
1305 if ($sub) {
1306 if (!$credentials instanceof ServiceAccountCredentials) {
1307 throw new DomainException('domain-wide authority requires service account credentials');
1308 }
1309
1310 $credentials->setSub($sub);
1311 }
1312
1313 // If we are not using FetchAuthTokenCache yet, create it now
1314 if (!$credentials instanceof FetchAuthTokenCache) {
1315 $credentials = new FetchAuthTokenCache(
1316 $credentials,
1317 $this->config['cache_config'],
1318 $this->getCache()
1319 );
1320 }
1321 return $credentials;
1322 }
1323
1324 protected function getAuthHandler()
1325 {
1326 // Be very careful using the cache, as the underlying auth library's cache
1327 // implementation is naive, and the cache keys do not account for user
1328 // sessions.
1329 //
1330 // @see https://github.com/google/google-api-php-client/issues/821
1331 return AuthHandlerFactory::build(
1332 $this->getCache(),
1333 $this->config['cache_config']
1334 );
1335 }
1336
1337 private function createUserRefreshCredentials($scope, $refreshToken)
1338 {
1339 $creds = array_filter([
1340 'client_id' => $this->getClientId(),
1341 'client_secret' => $this->getClientSecret(),
1342 'refresh_token' => $refreshToken,
1343 ]);
1344
1345 return new UserRefreshCredentials($scope, $creds);
1346 }
1347
1348 private function checkUniverseDomain($credentials)
1349 {
1350 $credentialsUniverse = $credentials instanceof GetUniverseDomainInterface
1351 ? $credentials->getUniverseDomain()
1352 : GetUniverseDomainInterface::DEFAULT_UNIVERSE_DOMAIN;
1353 if ($credentialsUniverse !== $this->getUniverseDomain()) {
1354 throw new DomainException(sprintf(
1355 'The configured universe domain (%s) does not match the credential universe domain (%s)',
1356 $this->getUniverseDomain(),
1357 $credentialsUniverse
1358 ));
1359 }
1360 }
1361
1362 public function getUniverseDomain()
1363 {
1364 return $this->config['universe_domain'];
1365 }
1366}
Note: See TracBrowser for help on using the repository browser.