vendor/google/apiclient/src/Client.php line 929

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