blob: 7da77a28f7af2262448a7cf0fafda2ad304c59dd [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
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070026from google.auth import environment_vars
27from google.auth import exceptions
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070028import google.auth.transport._http_client
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070029
30_LOGGER = logging.getLogger(__name__)
31
32# Valid types accepted for file-based credentials.
Bu Sun Kim9eec0912019-10-21 17:04:21 -070033_AUTHORIZED_USER_TYPE = "authorized_user"
34_SERVICE_ACCOUNT_TYPE = "service_account"
bojeil-googled4d7f382021-02-16 12:33:20 -080035_EXTERNAL_ACCOUNT_TYPE = "external_account"
36_VALID_TYPES = (_AUTHORIZED_USER_TYPE, _SERVICE_ACCOUNT_TYPE, _EXTERNAL_ACCOUNT_TYPE)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070037
38# Help message when no credentials can be found.
Thea Flowersa8d93482018-05-31 14:52:06 -070039_HELP_MESSAGE = """\
40Could not automatically determine credentials. Please set {env} or \
41explicitly create credentials and re-run the application. For more \
42information, please see \
Christopher Wilcoxf1028252018-09-21 10:03:04 -070043https://cloud.google.com/docs/authentication/getting-started
Bu Sun Kim9eec0912019-10-21 17:04:21 -070044""".format(
45 env=environment_vars.CREDENTIALS
46).strip()
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070047
Thea Flowersa8d93482018-05-31 14:52:06 -070048# Warning when using Cloud SDK user credentials
49_CLOUD_SDK_CREDENTIALS_WARNING = """\
50Your application has authenticated using end user credentials from Google \
arithmetic1728f30b45a2020-06-17 23:36:04 -070051Cloud SDK without a quota project. You might receive a "quota exceeded" \
52or "API not enabled" error. We recommend you rerun \
53`gcloud auth application-default login` and make sure a quota project is \
54added. Or you can use service accounts instead. For more information \
55about service accounts, see https://cloud.google.com/docs/authentication/"""
Thea Flowersa8d93482018-05-31 14:52:06 -070056
57
58def _warn_about_problematic_credentials(credentials):
59 """Determines if the credentials are problematic.
60
61 Credentials from the Cloud SDK that are associated with Cloud SDK's project
62 are problematic because they may not have APIs enabled and have limited
63 quota. If this is the case, warn about it.
64 """
65 from google.auth import _cloud_sdk
Bu Sun Kim9eec0912019-10-21 17:04:21 -070066
Thea Flowersa8d93482018-05-31 14:52:06 -070067 if credentials.client_id == _cloud_sdk.CLOUD_SDK_CLIENT_ID:
68 warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING)
69
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070070
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -070071def load_credentials_from_file(
bojeil-googled4d7f382021-02-16 12:33:20 -080072 filename, scopes=None, default_scopes=None, quota_project_id=None, request=None
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -070073):
Bu Sun Kim15d5fa92020-06-18 14:05:40 -070074 """Loads Google credentials from a file.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070075
bojeil-googled4d7f382021-02-16 12:33:20 -080076 The credentials file must be a service account key, stored authorized
77 user credentials or external account credentials.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070078
79 Args:
80 filename (str): The full path to the credentials file.
Bu Sun Kim15d5fa92020-06-18 14:05:40 -070081 scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If
82 specified, the credentials will automatically be scoped if
Bu Sun Kim3dda7b22020-07-09 10:39:39 -070083 necessary
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -070084 default_scopes (Optional[Sequence[str]]): Default scopes passed by a
85 Google client library. Use 'scopes' for user-defined scopes.
Bu Sun Kim3dda7b22020-07-09 10:39:39 -070086 quota_project_id (Optional[str]): The project ID used for
bojeil-googled4d7f382021-02-16 12:33:20 -080087 quota and billing.
88 request (Optional[google.auth.transport.Request]): An object used to make
89 HTTP requests. This is used to determine the associated project ID
90 for a workload identity pool resource (external account credentials).
91 If not specified, then it will use a
92 google.auth.transport.requests.Request client to make requests.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070093
94 Returns:
95 Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded
96 credentials and the project ID. Authorized user credentials do not
bojeil-googled4d7f382021-02-16 12:33:20 -080097 have the project ID information. External account credentials project
98 IDs may not always be determined.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -070099
100 Raises:
101 google.auth.exceptions.DefaultCredentialsError: if the file is in the
weitaiting6e86c932017-08-12 03:26:59 +0800102 wrong format or is missing.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700103 """
weitaiting6e86c932017-08-12 03:26:59 +0800104 if not os.path.exists(filename):
105 raise exceptions.DefaultCredentialsError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700106 "File {} was not found.".format(filename)
107 )
weitaiting6e86c932017-08-12 03:26:59 +0800108
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700109 with io.open(filename, "r") as file_obj:
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700110 try:
111 info = json.load(file_obj)
Danny Hermes895e3692017-11-09 11:35:57 -0800112 except ValueError as caught_exc:
113 new_exc = exceptions.DefaultCredentialsError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700114 "File {} is not a valid json file.".format(filename), caught_exc
115 )
Tres Seaver560cf1e2021-08-03 16:35:54 -0400116 raise new_exc from caught_exc
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700117
118 # The type key should indicate that the file is either a service account
119 # credentials file or an authorized user credentials file.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700120 credential_type = info.get("type")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700121
122 if credential_type == _AUTHORIZED_USER_TYPE:
arithmetic1728772dac62020-03-27 14:34:13 -0700123 from google.oauth2 import credentials
Jon Wayne Parrott6dca98c2016-12-01 15:34:59 -0800124
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700125 try:
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700126 credentials = credentials.Credentials.from_authorized_user_info(
127 info, scopes=scopes
Bu Sun Kimab2be5d2020-07-15 16:49:27 -0700128 )
Danny Hermes895e3692017-11-09 11:35:57 -0800129 except ValueError as caught_exc:
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700130 msg = "Failed to load authorized user credentials from {}".format(filename)
Danny Hermes0a93e872017-11-09 12:18:58 -0800131 new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
Tres Seaver560cf1e2021-08-03 16:35:54 -0400132 raise new_exc from caught_exc
Bu Sun Kimab2be5d2020-07-15 16:49:27 -0700133 if quota_project_id:
134 credentials = credentials.with_quota_project(quota_project_id)
arithmetic1728f30b45a2020-06-17 23:36:04 -0700135 if not credentials.quota_project_id:
136 _warn_about_problematic_credentials(credentials)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700137 return credentials, None
138
139 elif credential_type == _SERVICE_ACCOUNT_TYPE:
Jon Wayne Parrott6dca98c2016-12-01 15:34:59 -0800140 from google.oauth2 import service_account
141
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700142 try:
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700143 credentials = service_account.Credentials.from_service_account_info(
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700144 info, scopes=scopes, default_scopes=default_scopes
Bu Sun Kimab2be5d2020-07-15 16:49:27 -0700145 )
Danny Hermes895e3692017-11-09 11:35:57 -0800146 except ValueError as caught_exc:
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700147 msg = "Failed to load service account credentials from {}".format(filename)
Danny Hermes0a93e872017-11-09 12:18:58 -0800148 new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
Tres Seaver560cf1e2021-08-03 16:35:54 -0400149 raise new_exc from caught_exc
Bu Sun Kimab2be5d2020-07-15 16:49:27 -0700150 if quota_project_id:
151 credentials = credentials.with_quota_project(quota_project_id)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700152 return credentials, info.get("project_id")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700153
bojeil-googled4d7f382021-02-16 12:33:20 -0800154 elif credential_type == _EXTERNAL_ACCOUNT_TYPE:
155 credentials, project_id = _get_external_account_credentials(
156 info,
157 filename,
158 scopes=scopes,
159 default_scopes=default_scopes,
160 request=request,
161 )
162 if quota_project_id:
163 credentials = credentials.with_quota_project(quota_project_id)
164 return credentials, project_id
165
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700166 else:
167 raise exceptions.DefaultCredentialsError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700168 "The file {file} does not have a valid type. "
169 "Type is {type}, expected one of {valid_types}.".format(
170 file=filename, type=credential_type, valid_types=_VALID_TYPES
171 )
172 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700173
174
175def _get_gcloud_sdk_credentials():
176 """Gets the credentials and project ID from the Cloud SDK."""
Jon Wayne Parrott6dca98c2016-12-01 15:34:59 -0800177 from google.auth import _cloud_sdk
178
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400179 _LOGGER.debug("Checking Cloud SDK credentials as part of auth process...")
180
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700181 # Check if application default credentials exist.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700182 credentials_filename = _cloud_sdk.get_application_default_credentials_path()
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700183
184 if not os.path.isfile(credentials_filename):
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400185 _LOGGER.debug("Cloud SDK credentials not found on disk; not using them")
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700186 return None, None
187
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700188 credentials, project_id = load_credentials_from_file(credentials_filename)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700189
190 if not project_id:
191 project_id = _cloud_sdk.get_project_id()
192
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700193 return credentials, project_id
194
195
Bu Sun Kim9dc2d862021-02-11 12:53:26 -0700196def _get_explicit_environ_credentials():
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700197 """Gets credentials from the GOOGLE_APPLICATION_CREDENTIALS environment
Bu Sun Kim9dc2d862021-02-11 12:53:26 -0700198 variable."""
arithmetic1728333cb762021-02-25 15:42:32 -0800199 from google.auth import _cloud_sdk
200
201 cloud_sdk_adc_path = _cloud_sdk.get_application_default_credentials_path()
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700202 explicit_file = os.environ.get(environment_vars.CREDENTIALS)
203
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400204 _LOGGER.debug(
205 "Checking %s for explicit credentials as part of auth process...", explicit_file
206 )
207
arithmetic1728333cb762021-02-25 15:42:32 -0800208 if explicit_file is not None and explicit_file == cloud_sdk_adc_path:
209 # Cloud sdk flow calls gcloud to fetch project id, so if the explicit
210 # file path is cloud sdk credentials path, then we should fall back
211 # to cloud sdk flow, otherwise project id cannot be obtained.
212 _LOGGER.debug(
213 "Explicit credentials path %s is the same as Cloud SDK credentials path, fall back to Cloud SDK credentials flow...",
214 explicit_file,
215 )
216 return _get_gcloud_sdk_credentials()
217
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700218 if explicit_file is not None:
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700219 credentials, project_id = load_credentials_from_file(
Bu Sun Kim9dc2d862021-02-11 12:53:26 -0700220 os.environ[environment_vars.CREDENTIALS]
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700221 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700222
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700223 return credentials, project_id
224
225 else:
226 return None, None
227
228
229def _get_gae_credentials():
230 """Gets Google App Engine App Identity credentials and project ID."""
Zev Goldstein7f7d92d2021-07-23 15:52:46 -0400231 # If not GAE gen1, prefer the metadata service even if the GAE APIs are
232 # available as per https://google.aip.dev/auth/4115.
233 if os.environ.get(environment_vars.LEGACY_APPENGINE_RUNTIME) != "python27":
234 return None, None
235
James Wilson6e0781b2018-12-20 20:38:52 -0500236 # While this library is normally bundled with app_engine, there are
237 # some cases where it's not available, so we tolerate ImportError.
238 try:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400239 _LOGGER.debug("Checking for App Engine runtime as part of auth process...")
James Wilson6e0781b2018-12-20 20:38:52 -0500240 import google.auth.app_engine as app_engine
241 except ImportError:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400242 _LOGGER.warning("Import of App Engine auth library failed.")
James Wilson6e0781b2018-12-20 20:38:52 -0500243 return None, None
Jon Wayne Parrott6dca98c2016-12-01 15:34:59 -0800244
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -0700245 try:
246 credentials = app_engine.Credentials()
247 project_id = app_engine.get_project_id()
248 return credentials, project_id
249 except EnvironmentError:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400250 _LOGGER.debug(
251 "No App Engine library was found so cannot authentication via App Engine Identity Credentials."
252 )
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -0700253 return None, None
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700254
255
256def _get_gce_credentials(request=None):
257 """Gets credentials and project ID from the GCE Metadata Service."""
258 # Ping requires a transport, but we want application default credentials
259 # to require no arguments. So, we'll use the _http_client transport which
260 # uses http.client. This is only acceptable because the metadata server
261 # doesn't do SSL and never requires proxies.
James Wilson6e0781b2018-12-20 20:38:52 -0500262
263 # While this library is normally bundled with compute_engine, there are
264 # some cases where it's not available, so we tolerate ImportError.
265 try:
266 from google.auth import compute_engine
267 from google.auth.compute_engine import _metadata
268 except ImportError:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400269 _LOGGER.warning("Import of Compute Engine auth library failed.")
James Wilson6e0781b2018-12-20 20:38:52 -0500270 return None, None
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700271
272 if request is None:
273 request = google.auth.transport._http_client.Request()
274
275 if _metadata.ping(request=request):
276 # Get the project ID.
277 try:
Jon Wayne Parrott5b03ba12016-10-24 13:51:26 -0700278 project_id = _metadata.get_project_id(request=request)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700279 except exceptions.TransportError:
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700280 project_id = None
281
282 return compute_engine.Credentials(), project_id
283 else:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400284 _LOGGER.warning(
285 "Authentication failed using Compute Engine authentication due to unavailable metadata server."
286 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700287 return None, None
288
289
bojeil-googled4d7f382021-02-16 12:33:20 -0800290def _get_external_account_credentials(
291 info, filename, scopes=None, default_scopes=None, request=None
292):
293 """Loads external account Credentials from the parsed external account info.
294
295 The credentials information must correspond to a supported external account
296 credentials.
297
298 Args:
299 info (Mapping[str, str]): The external account info in Google format.
300 filename (str): The full path to the credentials file.
301 scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If
302 specified, the credentials will automatically be scoped if
303 necessary.
304 default_scopes (Optional[Sequence[str]]): Default scopes passed by a
305 Google client library. Use 'scopes' for user-defined scopes.
306 request (Optional[google.auth.transport.Request]): An object used to make
307 HTTP requests. This is used to determine the associated project ID
308 for a workload identity pool resource (external account credentials).
309 If not specified, then it will use a
310 google.auth.transport.requests.Request client to make requests.
311
312 Returns:
313 Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded
314 credentials and the project ID. External account credentials project
315 IDs may not always be determined.
316
317 Raises:
318 google.auth.exceptions.DefaultCredentialsError: if the info dictionary
319 is in the wrong format or is missing required information.
320 """
321 # There are currently 2 types of external_account credentials.
322 try:
323 # Check if configuration corresponds to an AWS credentials.
324 from google.auth import aws
325
326 credentials = aws.Credentials.from_info(
327 info, scopes=scopes, default_scopes=default_scopes
328 )
329 except ValueError:
330 try:
331 # Check if configuration corresponds to an Identity Pool credentials.
332 from google.auth import identity_pool
333
334 credentials = identity_pool.Credentials.from_info(
335 info, scopes=scopes, default_scopes=default_scopes
336 )
337 except ValueError:
338 # If the configuration is invalid or does not correspond to any
339 # supported external_account credentials, raise an error.
340 raise exceptions.DefaultCredentialsError(
341 "Failed to load external account credentials from {}".format(filename)
342 )
343 if request is None:
344 request = google.auth.transport.requests.Request()
345
346 return credentials, credentials.get_project_id(request=request)
347
348
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700349def default(scopes=None, request=None, quota_project_id=None, default_scopes=None):
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700350 """Gets the default credentials for the current environment.
351
352 `Application Default Credentials`_ provides an easy way to obtain
353 credentials to call Google APIs for server-to-server or local applications.
354 This function acquires credentials from the environment in the following
355 order:
356
357 1. If the environment variable ``GOOGLE_APPLICATION_CREDENTIALS`` is set
358 to the path of a valid service account JSON private key file, then it is
359 loaded and returned. The project ID returned is the project ID defined
360 in the service account file if available (some older files do not
361 contain project ID information).
bojeil-googled4d7f382021-02-16 12:33:20 -0800362
363 If the environment variable is set to the path of a valid external
364 account JSON configuration file (workload identity federation), then the
365 configuration file is used to determine and retrieve the external
366 credentials from the current environment (AWS, Azure, etc).
367 These will then be exchanged for Google access tokens via the Google STS
368 endpoint.
369 The project ID returned in this case is the one corresponding to the
370 underlying workload identity pool resource if determinable.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700371 2. If the `Google Cloud SDK`_ is installed and has application default
372 credentials set they are loaded and returned.
373
374 To enable application default credentials with the Cloud SDK run::
375
376 gcloud auth application-default login
377
378 If the Cloud SDK has an active project, the project ID is returned. The
379 active project can be set using::
380
381 gcloud config set project
382
383 3. If the application is running in the `App Engine standard environment`_
David Buxton0323cf32020-10-29 21:26:11 +0000384 (first generation) then the credentials and project ID from the
385 `App Identity Service`_ are used.
386 4. If the application is running in `Compute Engine`_ or `Cloud Run`_ or
387 the `App Engine flexible environment`_ or the `App Engine standard
388 environment`_ (second generation) then the credentials and project ID
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700389 are obtained from the `Metadata Service`_.
390 5. If no credentials are found,
391 :class:`~google.auth.exceptions.DefaultCredentialsError` will be raised.
392
393 .. _Application Default Credentials: https://developers.google.com\
394 /identity/protocols/application-default-credentials
395 .. _Google Cloud SDK: https://cloud.google.com/sdk
396 .. _App Engine standard environment: https://cloud.google.com/appengine
397 .. _App Identity Service: https://cloud.google.com/appengine/docs/python\
398 /appidentity/
399 .. _Compute Engine: https://cloud.google.com/compute
400 .. _App Engine flexible environment: https://cloud.google.com\
401 /appengine/flexible
402 .. _Metadata Service: https://cloud.google.com/compute/docs\
403 /storing-retrieving-metadata
David Buxton0323cf32020-10-29 21:26:11 +0000404 .. _Cloud Run: https://cloud.google.com/run
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700405
406 Example::
407
408 import google.auth
409
410 credentials, project_id = google.auth.default()
411
412 Args:
Jon Wayne Parrott8a7e5062016-11-07 16:45:17 -0800413 scopes (Sequence[str]): The list of scopes for the credentials. If
414 specified, the credentials will automatically be scoped if
415 necessary.
bojeil-googled4d7f382021-02-16 12:33:20 -0800416 request (Optional[google.auth.transport.Request]): An object used to make
417 HTTP requests. This is used to either detect whether the application
418 is running on Compute Engine or to determine the associated project
419 ID for a workload identity pool resource (external account
420 credentials). If not specified, then it will either use the standard
421 library http client to make requests for Compute Engine credentials
422 or a google.auth.transport.requests.Request client for external
423 account credentials.
424 quota_project_id (Optional[str]): The project ID used for
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700425 quota and billing.
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700426 default_scopes (Optional[Sequence[str]]): Default scopes passed by a
427 Google client library. Use 'scopes' for user-defined scopes.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700428 Returns:
429 Tuple[~google.auth.credentials.Credentials, Optional[str]]:
430 the current environment's credentials and project ID. Project ID
431 may be None, which indicates that the Project ID could not be
432 ascertained from the environment.
433
434 Raises:
435 ~google.auth.exceptions.DefaultCredentialsError:
436 If no credentials were found, or if the credentials found were
437 invalid.
438 """
Jon Wayne Parrott6dca98c2016-12-01 15:34:59 -0800439 from google.auth.credentials import with_scopes_if_required
440
Jon Wayne Parrottce37cba2016-11-07 16:41:42 -0800441 explicit_project_id = os.environ.get(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700442 environment_vars.PROJECT, os.environ.get(environment_vars.LEGACY_PROJECT)
443 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700444
445 checkers = (
bojeil-googled4d7f382021-02-16 12:33:20 -0800446 # Avoid passing scopes here to prevent passing scopes to user credentials.
447 # with_scopes_if_required() below will ensure scopes/default scopes are
448 # safely set on the returned credentials since requires_scopes will
449 # guard against setting scopes on user credentials.
Bu Sun Kim9dc2d862021-02-11 12:53:26 -0700450 _get_explicit_environ_credentials,
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700451 _get_gcloud_sdk_credentials,
452 _get_gae_credentials,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700453 lambda: _get_gce_credentials(request),
454 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700455
456 for checker in checkers:
457 credentials, project_id = checker()
458 if credentials is not None:
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700459 credentials = with_scopes_if_required(
460 credentials, scopes, default_scopes=default_scopes
461 )
bojeil-googled4d7f382021-02-16 12:33:20 -0800462
463 # For external account credentials, scopes are required to determine
464 # the project ID. Try to get the project ID again if not yet
465 # determined.
466 if not project_id and callable(
467 getattr(credentials, "get_project_id", None)
468 ):
469 if request is None:
470 request = google.auth.transport.requests.Request()
471 project_id = credentials.get_project_id(request=request)
472
Bu Sun Kimab2be5d2020-07-15 16:49:27 -0700473 if quota_project_id:
474 credentials = credentials.with_quota_project(quota_project_id)
475
Jacob Hayes15af07b2017-12-13 14:09:47 -0600476 effective_project_id = explicit_project_id or project_id
477 if not effective_project_id:
478 _LOGGER.warning(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700479 "No project ID could be determined. Consider running "
480 "`gcloud config set project` or setting the %s "
481 "environment variable",
482 environment_vars.PROJECT,
483 )
Jacob Hayes15af07b2017-12-13 14:09:47 -0600484 return credentials, effective_project_id
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700485
486 raise exceptions.DefaultCredentialsError(_HELP_MESSAGE)