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

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