blob: 8e3b2100d9fc0ee013ed88181a45ff9f66f4e430 [file] [log] [blame]
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -07001# Copyright 2015 Google Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Application default credentials.
16
17Implements application default credentials and project ID detection.
18"""
19
20import io
21import json
22import logging
23import os
Thea Flowersa8d93482018-05-31 14:52:06 -070024import warnings
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070025
Danny Hermes895e3692017-11-09 11:35:57 -080026import six
27
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070028from google.auth import environment_vars
29from google.auth import exceptions
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070030import google.auth.transport._http_client
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070031
32_LOGGER = logging.getLogger(__name__)
33
34# Valid types accepted for file-based credentials.
Bu Sun Kim9eec0912019-10-21 17:04:21 -070035_AUTHORIZED_USER_TYPE = "authorized_user"
36_SERVICE_ACCOUNT_TYPE = "service_account"
bojeil-googled4d7f382021-02-16 12:33:20 -080037_EXTERNAL_ACCOUNT_TYPE = "external_account"
38_VALID_TYPES = (_AUTHORIZED_USER_TYPE, _SERVICE_ACCOUNT_TYPE, _EXTERNAL_ACCOUNT_TYPE)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070039
40# Help message when no credentials can be found.
Thea Flowersa8d93482018-05-31 14:52:06 -070041_HELP_MESSAGE = """\
42Could not automatically determine credentials. Please set {env} or \
43explicitly create credentials and re-run the application. For more \
44information, please see \
Christopher Wilcoxf1028252018-09-21 10:03:04 -070045https://cloud.google.com/docs/authentication/getting-started
Bu Sun Kim9eec0912019-10-21 17:04:21 -070046""".format(
47 env=environment_vars.CREDENTIALS
48).strip()
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070049
Thea Flowersa8d93482018-05-31 14:52:06 -070050# Warning when using Cloud SDK user credentials
51_CLOUD_SDK_CREDENTIALS_WARNING = """\
52Your application has authenticated using end user credentials from Google \
arithmetic1728f30b45a2020-06-17 23:36:04 -070053Cloud SDK without a quota project. You might receive a "quota exceeded" \
54or "API not enabled" error. We recommend you rerun \
55`gcloud auth application-default login` and make sure a quota project is \
56added. Or you can use service accounts instead. For more information \
57about service accounts, see https://cloud.google.com/docs/authentication/"""
Thea Flowersa8d93482018-05-31 14:52:06 -070058
59
60def _warn_about_problematic_credentials(credentials):
61 """Determines if the credentials are problematic.
62
63 Credentials from the Cloud SDK that are associated with Cloud SDK's project
64 are problematic because they may not have APIs enabled and have limited
65 quota. If this is the case, warn about it.
66 """
67 from google.auth import _cloud_sdk
Bu Sun Kim9eec0912019-10-21 17:04:21 -070068
Thea Flowersa8d93482018-05-31 14:52:06 -070069 if credentials.client_id == _cloud_sdk.CLOUD_SDK_CLIENT_ID:
70 warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING)
71
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070072
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -070073def load_credentials_from_file(
bojeil-googled4d7f382021-02-16 12:33:20 -080074 filename, scopes=None, default_scopes=None, quota_project_id=None, request=None
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -070075):
Bu Sun Kim15d5fa92020-06-18 14:05:40 -070076 """Loads Google credentials from a file.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070077
bojeil-googled4d7f382021-02-16 12:33:20 -080078 The credentials file must be a service account key, stored authorized
79 user credentials or external account credentials.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070080
81 Args:
82 filename (str): The full path to the credentials file.
Bu Sun Kim15d5fa92020-06-18 14:05:40 -070083 scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If
84 specified, the credentials will automatically be scoped if
Bu Sun Kim3dda7b22020-07-09 10:39:39 -070085 necessary
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -070086 default_scopes (Optional[Sequence[str]]): Default scopes passed by a
87 Google client library. Use 'scopes' for user-defined scopes.
Bu Sun Kim3dda7b22020-07-09 10:39:39 -070088 quota_project_id (Optional[str]): The project ID used for
bojeil-googled4d7f382021-02-16 12:33:20 -080089 quota and billing.
90 request (Optional[google.auth.transport.Request]): An object used to make
91 HTTP requests. This is used to determine the associated project ID
92 for a workload identity pool resource (external account credentials).
93 If not specified, then it will use a
94 google.auth.transport.requests.Request client to make requests.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070095
96 Returns:
97 Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded
98 credentials and the project ID. Authorized user credentials do not
bojeil-googled4d7f382021-02-16 12:33:20 -080099 have the project ID information. External account credentials project
100 IDs may not always be determined.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700101
102 Raises:
103 google.auth.exceptions.DefaultCredentialsError: if the file is in the
weitaiting6e86c932017-08-12 03:26:59 +0800104 wrong format or is missing.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700105 """
weitaiting6e86c932017-08-12 03:26:59 +0800106 if not os.path.exists(filename):
107 raise exceptions.DefaultCredentialsError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700108 "File {} was not found.".format(filename)
109 )
weitaiting6e86c932017-08-12 03:26:59 +0800110
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700111 with io.open(filename, "r") as file_obj:
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700112 try:
113 info = json.load(file_obj)
Danny Hermes895e3692017-11-09 11:35:57 -0800114 except ValueError as caught_exc:
115 new_exc = exceptions.DefaultCredentialsError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700116 "File {} is not a valid json file.".format(filename), caught_exc
117 )
Danny Hermes895e3692017-11-09 11:35:57 -0800118 six.raise_from(new_exc, caught_exc)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700119
120 # The type key should indicate that the file is either a service account
121 # credentials file or an authorized user credentials file.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700122 credential_type = info.get("type")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700123
124 if credential_type == _AUTHORIZED_USER_TYPE:
arithmetic1728772dac62020-03-27 14:34:13 -0700125 from google.oauth2 import credentials
Jon Wayne Parrott6dca98c2016-12-01 15:34:59 -0800126
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700127 try:
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700128 credentials = credentials.Credentials.from_authorized_user_info(
129 info, scopes=scopes
Bu Sun Kimab2be5d2020-07-15 16:49:27 -0700130 )
Danny Hermes895e3692017-11-09 11:35:57 -0800131 except ValueError as caught_exc:
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700132 msg = "Failed to load authorized user credentials from {}".format(filename)
Danny Hermes0a93e872017-11-09 12:18:58 -0800133 new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
Danny Hermes895e3692017-11-09 11:35:57 -0800134 six.raise_from(new_exc, caught_exc)
Bu Sun Kimab2be5d2020-07-15 16:49:27 -0700135 if quota_project_id:
136 credentials = credentials.with_quota_project(quota_project_id)
arithmetic1728f30b45a2020-06-17 23:36:04 -0700137 if not credentials.quota_project_id:
138 _warn_about_problematic_credentials(credentials)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700139 return credentials, None
140
141 elif credential_type == _SERVICE_ACCOUNT_TYPE:
Jon Wayne Parrott6dca98c2016-12-01 15:34:59 -0800142 from google.oauth2 import service_account
143
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700144 try:
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700145 credentials = service_account.Credentials.from_service_account_info(
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700146 info, scopes=scopes, default_scopes=default_scopes
Bu Sun Kimab2be5d2020-07-15 16:49:27 -0700147 )
Danny Hermes895e3692017-11-09 11:35:57 -0800148 except ValueError as caught_exc:
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700149 msg = "Failed to load service account credentials from {}".format(filename)
Danny Hermes0a93e872017-11-09 12:18:58 -0800150 new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
Danny Hermes895e3692017-11-09 11:35:57 -0800151 six.raise_from(new_exc, caught_exc)
Bu Sun Kimab2be5d2020-07-15 16:49:27 -0700152 if quota_project_id:
153 credentials = credentials.with_quota_project(quota_project_id)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700154 return credentials, info.get("project_id")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700155
bojeil-googled4d7f382021-02-16 12:33:20 -0800156 elif credential_type == _EXTERNAL_ACCOUNT_TYPE:
157 credentials, project_id = _get_external_account_credentials(
158 info,
159 filename,
160 scopes=scopes,
161 default_scopes=default_scopes,
162 request=request,
163 )
164 if quota_project_id:
165 credentials = credentials.with_quota_project(quota_project_id)
166 return credentials, project_id
167
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700168 else:
169 raise exceptions.DefaultCredentialsError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700170 "The file {file} does not have a valid type. "
171 "Type is {type}, expected one of {valid_types}.".format(
172 file=filename, type=credential_type, valid_types=_VALID_TYPES
173 )
174 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700175
176
177def _get_gcloud_sdk_credentials():
178 """Gets the credentials and project ID from the Cloud SDK."""
Jon Wayne Parrott6dca98c2016-12-01 15:34:59 -0800179 from google.auth import _cloud_sdk
180
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400181 _LOGGER.debug("Checking Cloud SDK credentials as part of auth process...")
182
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700183 # Check if application default credentials exist.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700184 credentials_filename = _cloud_sdk.get_application_default_credentials_path()
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700185
186 if not os.path.isfile(credentials_filename):
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400187 _LOGGER.debug("Cloud SDK credentials not found on disk; not using them")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700188 return None, None
189
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700190 credentials, project_id = load_credentials_from_file(credentials_filename)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700191
192 if not project_id:
193 project_id = _cloud_sdk.get_project_id()
194
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700195 return credentials, project_id
196
197
Bu Sun Kim9dc2d862021-02-11 12:53:26 -0700198def _get_explicit_environ_credentials():
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700199 """Gets credentials from the GOOGLE_APPLICATION_CREDENTIALS environment
Bu Sun Kim9dc2d862021-02-11 12:53:26 -0700200 variable."""
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700201 explicit_file = os.environ.get(environment_vars.CREDENTIALS)
202
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400203 _LOGGER.debug(
204 "Checking %s for explicit credentials as part of auth process...", explicit_file
205 )
206
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700207 if explicit_file is not None:
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700208 credentials, project_id = load_credentials_from_file(
Bu Sun Kim9dc2d862021-02-11 12:53:26 -0700209 os.environ[environment_vars.CREDENTIALS]
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700210 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700211
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700212 return credentials, project_id
213
214 else:
215 return None, None
216
217
218def _get_gae_credentials():
219 """Gets Google App Engine App Identity credentials and project ID."""
James Wilson6e0781b2018-12-20 20:38:52 -0500220 # While this library is normally bundled with app_engine, there are
221 # some cases where it's not available, so we tolerate ImportError.
222 try:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400223 _LOGGER.debug("Checking for App Engine runtime as part of auth process...")
James Wilson6e0781b2018-12-20 20:38:52 -0500224 import google.auth.app_engine as app_engine
225 except ImportError:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400226 _LOGGER.warning("Import of App Engine auth library failed.")
James Wilson6e0781b2018-12-20 20:38:52 -0500227 return None, None
Jon Wayne Parrott6dca98c2016-12-01 15:34:59 -0800228
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -0700229 try:
230 credentials = app_engine.Credentials()
231 project_id = app_engine.get_project_id()
232 return credentials, project_id
233 except EnvironmentError:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400234 _LOGGER.debug(
235 "No App Engine library was found so cannot authentication via App Engine Identity Credentials."
236 )
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -0700237 return None, None
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700238
239
240def _get_gce_credentials(request=None):
241 """Gets credentials and project ID from the GCE Metadata Service."""
242 # Ping requires a transport, but we want application default credentials
243 # to require no arguments. So, we'll use the _http_client transport which
244 # uses http.client. This is only acceptable because the metadata server
245 # doesn't do SSL and never requires proxies.
James Wilson6e0781b2018-12-20 20:38:52 -0500246
247 # While this library is normally bundled with compute_engine, there are
248 # some cases where it's not available, so we tolerate ImportError.
249 try:
250 from google.auth import compute_engine
251 from google.auth.compute_engine import _metadata
252 except ImportError:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400253 _LOGGER.warning("Import of Compute Engine auth library failed.")
James Wilson6e0781b2018-12-20 20:38:52 -0500254 return None, None
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700255
256 if request is None:
257 request = google.auth.transport._http_client.Request()
258
259 if _metadata.ping(request=request):
260 # Get the project ID.
261 try:
Jon Wayne Parrott5b03ba12016-10-24 13:51:26 -0700262 project_id = _metadata.get_project_id(request=request)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700263 except exceptions.TransportError:
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700264 project_id = None
265
266 return compute_engine.Credentials(), project_id
267 else:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400268 _LOGGER.warning(
269 "Authentication failed using Compute Engine authentication due to unavailable metadata server."
270 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700271 return None, None
272
273
bojeil-googled4d7f382021-02-16 12:33:20 -0800274def _get_external_account_credentials(
275 info, filename, scopes=None, default_scopes=None, request=None
276):
277 """Loads external account Credentials from the parsed external account info.
278
279 The credentials information must correspond to a supported external account
280 credentials.
281
282 Args:
283 info (Mapping[str, str]): The external account info in Google format.
284 filename (str): The full path to the credentials file.
285 scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If
286 specified, the credentials will automatically be scoped if
287 necessary.
288 default_scopes (Optional[Sequence[str]]): Default scopes passed by a
289 Google client library. Use 'scopes' for user-defined scopes.
290 request (Optional[google.auth.transport.Request]): An object used to make
291 HTTP requests. This is used to determine the associated project ID
292 for a workload identity pool resource (external account credentials).
293 If not specified, then it will use a
294 google.auth.transport.requests.Request client to make requests.
295
296 Returns:
297 Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded
298 credentials and the project ID. External account credentials project
299 IDs may not always be determined.
300
301 Raises:
302 google.auth.exceptions.DefaultCredentialsError: if the info dictionary
303 is in the wrong format or is missing required information.
304 """
305 # There are currently 2 types of external_account credentials.
306 try:
307 # Check if configuration corresponds to an AWS credentials.
308 from google.auth import aws
309
310 credentials = aws.Credentials.from_info(
311 info, scopes=scopes, default_scopes=default_scopes
312 )
313 except ValueError:
314 try:
315 # Check if configuration corresponds to an Identity Pool credentials.
316 from google.auth import identity_pool
317
318 credentials = identity_pool.Credentials.from_info(
319 info, scopes=scopes, default_scopes=default_scopes
320 )
321 except ValueError:
322 # If the configuration is invalid or does not correspond to any
323 # supported external_account credentials, raise an error.
324 raise exceptions.DefaultCredentialsError(
325 "Failed to load external account credentials from {}".format(filename)
326 )
327 if request is None:
328 request = google.auth.transport.requests.Request()
329
330 return credentials, credentials.get_project_id(request=request)
331
332
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700333def default(scopes=None, request=None, quota_project_id=None, default_scopes=None):
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700334 """Gets the default credentials for the current environment.
335
336 `Application Default Credentials`_ provides an easy way to obtain
337 credentials to call Google APIs for server-to-server or local applications.
338 This function acquires credentials from the environment in the following
339 order:
340
341 1. If the environment variable ``GOOGLE_APPLICATION_CREDENTIALS`` is set
342 to the path of a valid service account JSON private key file, then it is
343 loaded and returned. The project ID returned is the project ID defined
344 in the service account file if available (some older files do not
345 contain project ID information).
bojeil-googled4d7f382021-02-16 12:33:20 -0800346
347 If the environment variable is set to the path of a valid external
348 account JSON configuration file (workload identity federation), then the
349 configuration file is used to determine and retrieve the external
350 credentials from the current environment (AWS, Azure, etc).
351 These will then be exchanged for Google access tokens via the Google STS
352 endpoint.
353 The project ID returned in this case is the one corresponding to the
354 underlying workload identity pool resource if determinable.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700355 2. If the `Google Cloud SDK`_ is installed and has application default
356 credentials set they are loaded and returned.
357
358 To enable application default credentials with the Cloud SDK run::
359
360 gcloud auth application-default login
361
362 If the Cloud SDK has an active project, the project ID is returned. The
363 active project can be set using::
364
365 gcloud config set project
366
367 3. If the application is running in the `App Engine standard environment`_
David Buxton0323cf32020-10-29 21:26:11 +0000368 (first generation) then the credentials and project ID from the
369 `App Identity Service`_ are used.
370 4. If the application is running in `Compute Engine`_ or `Cloud Run`_ or
371 the `App Engine flexible environment`_ or the `App Engine standard
372 environment`_ (second generation) then the credentials and project ID
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700373 are obtained from the `Metadata Service`_.
374 5. If no credentials are found,
375 :class:`~google.auth.exceptions.DefaultCredentialsError` will be raised.
376
377 .. _Application Default Credentials: https://developers.google.com\
378 /identity/protocols/application-default-credentials
379 .. _Google Cloud SDK: https://cloud.google.com/sdk
380 .. _App Engine standard environment: https://cloud.google.com/appengine
381 .. _App Identity Service: https://cloud.google.com/appengine/docs/python\
382 /appidentity/
383 .. _Compute Engine: https://cloud.google.com/compute
384 .. _App Engine flexible environment: https://cloud.google.com\
385 /appengine/flexible
386 .. _Metadata Service: https://cloud.google.com/compute/docs\
387 /storing-retrieving-metadata
David Buxton0323cf32020-10-29 21:26:11 +0000388 .. _Cloud Run: https://cloud.google.com/run
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700389
390 Example::
391
392 import google.auth
393
394 credentials, project_id = google.auth.default()
395
396 Args:
Jon Wayne Parrott8a7e5062016-11-07 16:45:17 -0800397 scopes (Sequence[str]): The list of scopes for the credentials. If
398 specified, the credentials will automatically be scoped if
399 necessary.
bojeil-googled4d7f382021-02-16 12:33:20 -0800400 request (Optional[google.auth.transport.Request]): An object used to make
401 HTTP requests. This is used to either detect whether the application
402 is running on Compute Engine or to determine the associated project
403 ID for a workload identity pool resource (external account
404 credentials). If not specified, then it will either use the standard
405 library http client to make requests for Compute Engine credentials
406 or a google.auth.transport.requests.Request client for external
407 account credentials.
408 quota_project_id (Optional[str]): The project ID used for
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700409 quota and billing.
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700410 default_scopes (Optional[Sequence[str]]): Default scopes passed by a
411 Google client library. Use 'scopes' for user-defined scopes.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700412 Returns:
413 Tuple[~google.auth.credentials.Credentials, Optional[str]]:
414 the current environment's credentials and project ID. Project ID
415 may be None, which indicates that the Project ID could not be
416 ascertained from the environment.
417
418 Raises:
419 ~google.auth.exceptions.DefaultCredentialsError:
420 If no credentials were found, or if the credentials found were
421 invalid.
422 """
Jon Wayne Parrott6dca98c2016-12-01 15:34:59 -0800423 from google.auth.credentials import with_scopes_if_required
424
Jon Wayne Parrottce37cba2016-11-07 16:41:42 -0800425 explicit_project_id = os.environ.get(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700426 environment_vars.PROJECT, os.environ.get(environment_vars.LEGACY_PROJECT)
427 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700428
429 checkers = (
bojeil-googled4d7f382021-02-16 12:33:20 -0800430 # Avoid passing scopes here to prevent passing scopes to user credentials.
431 # with_scopes_if_required() below will ensure scopes/default scopes are
432 # safely set on the returned credentials since requires_scopes will
433 # guard against setting scopes on user credentials.
Bu Sun Kim9dc2d862021-02-11 12:53:26 -0700434 _get_explicit_environ_credentials,
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700435 _get_gcloud_sdk_credentials,
436 _get_gae_credentials,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700437 lambda: _get_gce_credentials(request),
438 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700439
440 for checker in checkers:
441 credentials, project_id = checker()
442 if credentials is not None:
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700443 credentials = with_scopes_if_required(
444 credentials, scopes, default_scopes=default_scopes
445 )
bojeil-googled4d7f382021-02-16 12:33:20 -0800446
447 # For external account credentials, scopes are required to determine
448 # the project ID. Try to get the project ID again if not yet
449 # determined.
450 if not project_id and callable(
451 getattr(credentials, "get_project_id", None)
452 ):
453 if request is None:
454 request = google.auth.transport.requests.Request()
455 project_id = credentials.get_project_id(request=request)
456
Bu Sun Kimab2be5d2020-07-15 16:49:27 -0700457 if quota_project_id:
458 credentials = credentials.with_quota_project(quota_project_id)
459
Jacob Hayes15af07b2017-12-13 14:09:47 -0600460 effective_project_id = explicit_project_id or project_id
461 if not effective_project_id:
462 _LOGGER.warning(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700463 "No project ID could be determined. Consider running "
464 "`gcloud config set project` or setting the %s "
465 "environment variable",
466 environment_vars.PROJECT,
467 )
Jacob Hayes15af07b2017-12-13 14:09:47 -0600468 return credentials, effective_project_id
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700469
470 raise exceptions.DefaultCredentialsError(_HELP_MESSAGE)