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