ApplicationDefaultCredentials.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. <?php
  2. /*
  3. * Copyright 2015 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\Auth;
  18. use DomainException;
  19. use Google\Auth\Credentials\AppIdentityCredentials;
  20. use Google\Auth\Credentials\GCECredentials;
  21. use Google\Auth\Credentials\ServiceAccountCredentials;
  22. use Google\Auth\HttpHandler\HttpClientCache;
  23. use Google\Auth\HttpHandler\HttpHandlerFactory;
  24. use Google\Auth\Middleware\AuthTokenMiddleware;
  25. use Google\Auth\Middleware\ProxyAuthTokenMiddleware;
  26. use Google\Auth\Subscriber\AuthTokenSubscriber;
  27. use GuzzleHttp\Client;
  28. use InvalidArgumentException;
  29. use Psr\Cache\CacheItemPoolInterface;
  30. /**
  31. * ApplicationDefaultCredentials obtains the default credentials for
  32. * authorizing a request to a Google service.
  33. *
  34. * Application Default Credentials are described here:
  35. * https://developers.google.com/accounts/docs/application-default-credentials
  36. *
  37. * This class implements the search for the application default credentials as
  38. * described in the link.
  39. *
  40. * It provides three factory methods:
  41. * - #get returns the computed credentials object
  42. * - #getSubscriber returns an AuthTokenSubscriber built from the credentials object
  43. * - #getMiddleware returns an AuthTokenMiddleware built from the credentials object
  44. *
  45. * This allows it to be used as follows with GuzzleHttp\Client:
  46. *
  47. * ```
  48. * use Google\Auth\ApplicationDefaultCredentials;
  49. * use GuzzleHttp\Client;
  50. * use GuzzleHttp\HandlerStack;
  51. *
  52. * $middleware = ApplicationDefaultCredentials::getMiddleware(
  53. * 'https://www.googleapis.com/auth/taskqueue'
  54. * );
  55. * $stack = HandlerStack::create();
  56. * $stack->push($middleware);
  57. *
  58. * $client = new Client([
  59. * 'handler' => $stack,
  60. * 'base_uri' => 'https://www.googleapis.com/taskqueue/v1beta2/projects/',
  61. * 'auth' => 'google_auth' // authorize all requests
  62. * ]);
  63. *
  64. * $res = $client->get('myproject/taskqueues/myqueue');
  65. * ```
  66. */
  67. class ApplicationDefaultCredentials
  68. {
  69. /**
  70. * @deprecated
  71. *
  72. * Obtains an AuthTokenSubscriber that uses the default FetchAuthTokenInterface
  73. * implementation to use in this environment.
  74. *
  75. * If supplied, $scope is used to in creating the credentials instance if
  76. * this does not fallback to the compute engine defaults.
  77. *
  78. * @param string|string[] $scope the scope of the access request, expressed
  79. * either as an Array or as a space-delimited String.
  80. * @param callable $httpHandler callback which delivers psr7 request
  81. * @param array<mixed> $cacheConfig configuration for the cache when it's present
  82. * @param CacheItemPoolInterface $cache A cache implementation, may be
  83. * provided if you have one already available for use.
  84. * @return AuthTokenSubscriber
  85. * @throws DomainException if no implementation can be obtained.
  86. */
  87. public static function getSubscriber(// @phpstan-ignore-line
  88. $scope = null,
  89. callable $httpHandler = null,
  90. array $cacheConfig = null,
  91. CacheItemPoolInterface $cache = null
  92. ) {
  93. $creds = self::getCredentials($scope, $httpHandler, $cacheConfig, $cache);
  94. /** @phpstan-ignore-next-line */
  95. return new AuthTokenSubscriber($creds, $httpHandler);
  96. }
  97. /**
  98. * Obtains an AuthTokenMiddleware that uses the default FetchAuthTokenInterface
  99. * implementation to use in this environment.
  100. *
  101. * If supplied, $scope is used to in creating the credentials instance if
  102. * this does not fallback to the compute engine defaults.
  103. *
  104. * @param string|string[] $scope the scope of the access request, expressed
  105. * either as an Array or as a space-delimited String.
  106. * @param callable $httpHandler callback which delivers psr7 request
  107. * @param array<mixed> $cacheConfig configuration for the cache when it's present
  108. * @param CacheItemPoolInterface $cache A cache implementation, may be
  109. * provided if you have one already available for use.
  110. * @param string $quotaProject specifies a project to bill for access
  111. * charges associated with the request.
  112. * @return AuthTokenMiddleware
  113. * @throws DomainException if no implementation can be obtained.
  114. */
  115. public static function getMiddleware(
  116. $scope = null,
  117. callable $httpHandler = null,
  118. array $cacheConfig = null,
  119. CacheItemPoolInterface $cache = null,
  120. $quotaProject = null
  121. ) {
  122. $creds = self::getCredentials($scope, $httpHandler, $cacheConfig, $cache, $quotaProject);
  123. return new AuthTokenMiddleware($creds, $httpHandler);
  124. }
  125. /**
  126. * Obtains the default FetchAuthTokenInterface implementation to use
  127. * in this environment.
  128. *
  129. * @param string|string[] $scope the scope of the access request, expressed
  130. * either as an Array or as a space-delimited String.
  131. * @param callable $httpHandler callback which delivers psr7 request
  132. * @param array<mixed> $cacheConfig configuration for the cache when it's present
  133. * @param CacheItemPoolInterface $cache A cache implementation, may be
  134. * provided if you have one already available for use.
  135. * @param string $quotaProject specifies a project to bill for access
  136. * charges associated with the request.
  137. * @param string|string[] $defaultScope The default scope to use if no
  138. * user-defined scopes exist, expressed either as an Array or as a
  139. * space-delimited string.
  140. *
  141. * @return FetchAuthTokenInterface
  142. * @throws DomainException if no implementation can be obtained.
  143. */
  144. public static function getCredentials(
  145. $scope = null,
  146. callable $httpHandler = null,
  147. array $cacheConfig = null,
  148. CacheItemPoolInterface $cache = null,
  149. $quotaProject = null,
  150. $defaultScope = null
  151. ) {
  152. $creds = null;
  153. $jsonKey = CredentialsLoader::fromEnv()
  154. ?: CredentialsLoader::fromWellKnownFile();
  155. $anyScope = $scope ?: $defaultScope;
  156. if (!$httpHandler) {
  157. if (!($client = HttpClientCache::getHttpClient())) {
  158. $client = new Client();
  159. HttpClientCache::setHttpClient($client);
  160. }
  161. $httpHandler = HttpHandlerFactory::build($client);
  162. }
  163. if (!is_null($jsonKey)) {
  164. if ($quotaProject) {
  165. $jsonKey['quota_project_id'] = $quotaProject;
  166. }
  167. $creds = CredentialsLoader::makeCredentials(
  168. $scope,
  169. $jsonKey,
  170. $defaultScope
  171. );
  172. } elseif (AppIdentityCredentials::onAppEngine() && !GCECredentials::onAppEngineFlexible()) {
  173. $creds = new AppIdentityCredentials($anyScope);
  174. } elseif (self::onGce($httpHandler, $cacheConfig, $cache)) {
  175. $creds = new GCECredentials(null, $anyScope, null, $quotaProject);
  176. }
  177. if (is_null($creds)) {
  178. throw new DomainException(self::notFound());
  179. }
  180. if (!is_null($cache)) {
  181. $creds = new FetchAuthTokenCache($creds, $cacheConfig, $cache);
  182. }
  183. return $creds;
  184. }
  185. /**
  186. * Obtains an AuthTokenMiddleware which will fetch an ID token to use in the
  187. * Authorization header. The middleware is configured with the default
  188. * FetchAuthTokenInterface implementation to use in this environment.
  189. *
  190. * If supplied, $targetAudience is used to set the "aud" on the resulting
  191. * ID token.
  192. *
  193. * @param string $targetAudience The audience for the ID token.
  194. * @param callable $httpHandler callback which delivers psr7 request
  195. * @param array<mixed> $cacheConfig configuration for the cache when it's present
  196. * @param CacheItemPoolInterface $cache A cache implementation, may be
  197. * provided if you have one already available for use.
  198. * @return AuthTokenMiddleware
  199. * @throws DomainException if no implementation can be obtained.
  200. */
  201. public static function getIdTokenMiddleware(
  202. $targetAudience,
  203. callable $httpHandler = null,
  204. array $cacheConfig = null,
  205. CacheItemPoolInterface $cache = null
  206. ) {
  207. $creds = self::getIdTokenCredentials($targetAudience, $httpHandler, $cacheConfig, $cache);
  208. return new AuthTokenMiddleware($creds, $httpHandler);
  209. }
  210. /**
  211. * Obtains an ProxyAuthTokenMiddleware which will fetch an ID token to use in the
  212. * Authorization header. The middleware is configured with the default
  213. * FetchAuthTokenInterface implementation to use in this environment.
  214. *
  215. * If supplied, $targetAudience is used to set the "aud" on the resulting
  216. * ID token.
  217. *
  218. * @param string $targetAudience The audience for the ID token.
  219. * @param callable $httpHandler callback which delivers psr7 request
  220. * @param array<mixed> $cacheConfig configuration for the cache when it's present
  221. * @param CacheItemPoolInterface $cache A cache implementation, may be
  222. * provided if you have one already available for use.
  223. * @return ProxyAuthTokenMiddleware
  224. * @throws DomainException if no implementation can be obtained.
  225. */
  226. public static function getProxyIdTokenMiddleware(
  227. $targetAudience,
  228. callable $httpHandler = null,
  229. array $cacheConfig = null,
  230. CacheItemPoolInterface $cache = null
  231. ) {
  232. $creds = self::getIdTokenCredentials($targetAudience, $httpHandler, $cacheConfig, $cache);
  233. return new ProxyAuthTokenMiddleware($creds, $httpHandler);
  234. }
  235. /**
  236. * Obtains the default FetchAuthTokenInterface implementation to use
  237. * in this environment, configured with a $targetAudience for fetching an ID
  238. * token.
  239. *
  240. * @param string $targetAudience The audience for the ID token.
  241. * @param callable $httpHandler callback which delivers psr7 request
  242. * @param array<mixed> $cacheConfig configuration for the cache when it's present
  243. * @param CacheItemPoolInterface $cache A cache implementation, may be
  244. * provided if you have one already available for use.
  245. * @return FetchAuthTokenInterface
  246. * @throws DomainException if no implementation can be obtained.
  247. * @throws InvalidArgumentException if JSON "type" key is invalid
  248. */
  249. public static function getIdTokenCredentials(
  250. $targetAudience,
  251. callable $httpHandler = null,
  252. array $cacheConfig = null,
  253. CacheItemPoolInterface $cache = null
  254. ) {
  255. $creds = null;
  256. $jsonKey = CredentialsLoader::fromEnv()
  257. ?: CredentialsLoader::fromWellKnownFile();
  258. if (!$httpHandler) {
  259. if (!($client = HttpClientCache::getHttpClient())) {
  260. $client = new Client();
  261. HttpClientCache::setHttpClient($client);
  262. }
  263. $httpHandler = HttpHandlerFactory::build($client);
  264. }
  265. if (!is_null($jsonKey)) {
  266. if (!array_key_exists('type', $jsonKey)) {
  267. throw new \InvalidArgumentException('json key is missing the type field');
  268. }
  269. if ($jsonKey['type'] == 'authorized_user') {
  270. throw new InvalidArgumentException('ID tokens are not supported for end user credentials');
  271. }
  272. if ($jsonKey['type'] != 'service_account') {
  273. throw new InvalidArgumentException('invalid value in the type field');
  274. }
  275. $creds = new ServiceAccountCredentials(null, $jsonKey, null, $targetAudience);
  276. } elseif (self::onGce($httpHandler, $cacheConfig, $cache)) {
  277. $creds = new GCECredentials(null, null, $targetAudience);
  278. }
  279. if (is_null($creds)) {
  280. throw new DomainException(self::notFound());
  281. }
  282. if (!is_null($cache)) {
  283. $creds = new FetchAuthTokenCache($creds, $cacheConfig, $cache);
  284. }
  285. return $creds;
  286. }
  287. /**
  288. * @return string
  289. */
  290. private static function notFound()
  291. {
  292. $msg = 'Could not load the default credentials. Browse to ';
  293. $msg .= 'https://developers.google.com';
  294. $msg .= '/accounts/docs/application-default-credentials';
  295. $msg .= ' for more information';
  296. return $msg;
  297. }
  298. /**
  299. * @param callable $httpHandler
  300. * @param array<mixed> $cacheConfig
  301. * @param CacheItemPoolInterface $cache
  302. * @return bool
  303. */
  304. private static function onGce(
  305. callable $httpHandler = null,
  306. array $cacheConfig = null,
  307. CacheItemPoolInterface $cache = null
  308. ) {
  309. $gceCacheConfig = [];
  310. foreach (['lifetime', 'prefix'] as $key) {
  311. if (isset($cacheConfig['gce_' . $key])) {
  312. $gceCacheConfig[$key] = $cacheConfig['gce_' . $key];
  313. }
  314. }
  315. return (new GCECache($gceCacheConfig, $cache))->onGce($httpHandler);
  316. }
  317. }