Нет описания

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