PluginProbe
WP-Stateless – Google Cloud Storage / trunk
WP-Stateless – Google Cloud Storage vtrunk
4.4.3 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.3.0 2.3.1 2.3.2 3.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 All 62 releases
wp-stateless / lib / Google / src / Client.php

Client.php in WP-Stateless – Google Cloud Storage trunk, at lib/Google/src/Client.php

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