1# 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
24import warnings
25
26from google.auth import environment_vars
27from google.auth import exceptions
28import google.auth.transport._http_client
29
30_LOGGER = logging.getLogger(__name__)
31
32# Valid types accepted for file-based credentials.
33_AUTHORIZED_USER_TYPE = "authorized_user"
34_SERVICE_ACCOUNT_TYPE = "service_account"
35_EXTERNAL_ACCOUNT_TYPE = "external_account"
36_EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE = "external_account_authorized_user"
37_IMPERSONATED_SERVICE_ACCOUNT_TYPE = "impersonated_service_account"
38_GDCH_SERVICE_ACCOUNT_TYPE = "gdch_service_account"
39_VALID_TYPES = (
40 _AUTHORIZED_USER_TYPE,
41 _SERVICE_ACCOUNT_TYPE,
42 _EXTERNAL_ACCOUNT_TYPE,
43 _EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE,
44 _IMPERSONATED_SERVICE_ACCOUNT_TYPE,
45 _GDCH_SERVICE_ACCOUNT_TYPE,
46)
47
48# Help message when no credentials can be found.
49_CLOUD_SDK_MISSING_CREDENTIALS = """\
50Your default credentials were not found. To set up Application Default Credentials, \
51see https://cloud.google.com/docs/authentication/external/set-up-adc for more information.\
52"""
53
54# Warning when using Cloud SDK user credentials
55_CLOUD_SDK_CREDENTIALS_WARNING = """\
56Your application has authenticated using end user credentials from Google \
57Cloud SDK without a quota project. You might receive a "quota exceeded" \
58or "API not enabled" error. See the following page for troubleshooting: \
59https://cloud.google.com/docs/authentication/adc-troubleshooting/user-creds. \
60"""
61
62# The subject token type used for AWS external_account credentials.
63_AWS_SUBJECT_TOKEN_TYPE = "urn:ietf:params:aws:token-type:aws4_request"
64
65
66def _warn_about_problematic_credentials(credentials):
67 """Determines if the credentials are problematic.
68
69 Credentials from the Cloud SDK that are associated with Cloud SDK's project
70 are problematic because they may not have APIs enabled and have limited
71 quota. If this is the case, warn about it.
72 """
73 from google.auth import _cloud_sdk
74
75 if credentials.client_id == _cloud_sdk.CLOUD_SDK_CLIENT_ID:
76 warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING)
77
78
79def load_credentials_from_file(
80 filename, scopes=None, default_scopes=None, quota_project_id=None, request=None
81):
82 """Loads Google credentials from a file.
83
84 The credentials file must be a service account key, stored authorized
85 user credentials, external account credentials, or impersonated service
86 account credentials.
87
88 .. warning::
89 Important: If you accept a credential configuration (credential JSON/File/Stream)
90 from an external source for authentication to Google Cloud Platform, you must
91 validate it before providing it to any Google API or client library. Providing an
92 unvalidated credential configuration to Google APIs or libraries can compromise
93 the security of your systems and data. For more information, refer to
94 `Validate credential configurations from external sources`_.
95
96 .. _Validate credential configurations from external sources:
97 https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
98
99 Args:
100 filename (str): The full path to the credentials file.
101 scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If
102 specified, the credentials will automatically be scoped if
103 necessary
104 default_scopes (Optional[Sequence[str]]): Default scopes passed by a
105 Google client library. Use 'scopes' for user-defined scopes.
106 quota_project_id (Optional[str]): The project ID used for
107 quota and billing.
108 request (Optional[google.auth.transport.Request]): An object used to make
109 HTTP requests. This is used to determine the associated project ID
110 for a workload identity pool resource (external account credentials).
111 If not specified, then it will use a
112 google.auth.transport.requests.Request client to make requests.
113
114 Returns:
115 Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded
116 credentials and the project ID. Authorized user credentials do not
117 have the project ID information. External account credentials project
118 IDs may not always be determined.
119
120 Raises:
121 google.auth.exceptions.DefaultCredentialsError: if the file is in the
122 wrong format or is missing.
123 """
124 if not os.path.exists(filename):
125 raise exceptions.DefaultCredentialsError(
126 "File {} was not found.".format(filename)
127 )
128
129 with io.open(filename, "r") as file_obj:
130 try:
131 info = json.load(file_obj)
132 except ValueError as caught_exc:
133 new_exc = exceptions.DefaultCredentialsError(
134 "File {} is not a valid json file.".format(filename), caught_exc
135 )
136 raise new_exc from caught_exc
137 return _load_credentials_from_info(
138 filename, info, scopes, default_scopes, quota_project_id, request
139 )
140
141
142def load_credentials_from_dict(
143 info, scopes=None, default_scopes=None, quota_project_id=None, request=None
144):
145 """Loads Google credentials from a dict.
146
147 The credentials file must be a service account key, stored authorized
148 user credentials, external account credentials, or impersonated service
149 account credentials.
150
151 .. warning::
152 Important: If you accept a credential configuration (credential JSON/File/Stream)
153 from an external source for authentication to Google Cloud Platform, you must
154 validate it before providing it to any Google API or client library. Providing an
155 unvalidated credential configuration to Google APIs or libraries can compromise
156 the security of your systems and data. For more information, refer to
157 `Validate credential configurations from external sources`_.
158
159 .. _Validate credential configurations from external sources:
160 https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
161
162 Args:
163 info (Dict[str, Any]): A dict object containing the credentials
164 scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If
165 specified, the credentials will automatically be scoped if
166 necessary
167 default_scopes (Optional[Sequence[str]]): Default scopes passed by a
168 Google client library. Use 'scopes' for user-defined scopes.
169 quota_project_id (Optional[str]): The project ID used for
170 quota and billing.
171 request (Optional[google.auth.transport.Request]): An object used to make
172 HTTP requests. This is used to determine the associated project ID
173 for a workload identity pool resource (external account credentials).
174 If not specified, then it will use a
175 google.auth.transport.requests.Request client to make requests.
176
177 Returns:
178 Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded
179 credentials and the project ID. Authorized user credentials do not
180 have the project ID information. External account credentials project
181 IDs may not always be determined.
182
183 Raises:
184 google.auth.exceptions.DefaultCredentialsError: if the file is in the
185 wrong format or is missing.
186 """
187 if not isinstance(info, dict):
188 raise exceptions.DefaultCredentialsError(
189 "info object was of type {} but dict type was expected.".format(type(info))
190 )
191
192 return _load_credentials_from_info(
193 "dict object", info, scopes, default_scopes, quota_project_id, request
194 )
195
196
197def _load_credentials_from_info(
198 filename, info, scopes, default_scopes, quota_project_id, request
199):
200 from google.auth.credentials import CredentialsWithQuotaProject
201
202 credential_type = info.get("type")
203
204 if credential_type == _AUTHORIZED_USER_TYPE:
205 credentials, project_id = _get_authorized_user_credentials(
206 filename, info, scopes
207 )
208
209 elif credential_type == _SERVICE_ACCOUNT_TYPE:
210 credentials, project_id = _get_service_account_credentials(
211 filename, info, scopes, default_scopes
212 )
213
214 elif credential_type == _EXTERNAL_ACCOUNT_TYPE:
215 credentials, project_id = _get_external_account_credentials(
216 info,
217 filename,
218 scopes=scopes,
219 default_scopes=default_scopes,
220 request=request,
221 )
222
223 elif credential_type == _EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE:
224 credentials, project_id = _get_external_account_authorized_user_credentials(
225 filename, info, request
226 )
227
228 elif credential_type == _IMPERSONATED_SERVICE_ACCOUNT_TYPE:
229 credentials, project_id = _get_impersonated_service_account_credentials(
230 filename, info, scopes
231 )
232 elif credential_type == _GDCH_SERVICE_ACCOUNT_TYPE:
233 credentials, project_id = _get_gdch_service_account_credentials(filename, info)
234 else:
235 raise exceptions.DefaultCredentialsError(
236 "The file {file} does not have a valid type. "
237 "Type is {type}, expected one of {valid_types}.".format(
238 file=filename, type=credential_type, valid_types=_VALID_TYPES
239 )
240 )
241 if isinstance(credentials, CredentialsWithQuotaProject):
242 credentials = _apply_quota_project_id(credentials, quota_project_id)
243 return credentials, project_id
244
245
246def _get_gcloud_sdk_credentials(quota_project_id=None):
247 """Gets the credentials and project ID from the Cloud SDK."""
248 from google.auth import _cloud_sdk
249
250 _LOGGER.debug("Checking Cloud SDK credentials as part of auth process...")
251
252 # Check if application default credentials exist.
253 credentials_filename = _cloud_sdk.get_application_default_credentials_path()
254
255 if not os.path.isfile(credentials_filename):
256 _LOGGER.debug("Cloud SDK credentials not found on disk; not using them")
257 return None, None
258
259 credentials, project_id = load_credentials_from_file(
260 credentials_filename, quota_project_id=quota_project_id
261 )
262 credentials._cred_file_path = credentials_filename
263
264 if not project_id:
265 project_id = _cloud_sdk.get_project_id()
266
267 return credentials, project_id
268
269
270def _get_explicit_environ_credentials(quota_project_id=None):
271 """Gets credentials from the GOOGLE_APPLICATION_CREDENTIALS environment
272 variable."""
273 from google.auth import _cloud_sdk
274
275 cloud_sdk_adc_path = _cloud_sdk.get_application_default_credentials_path()
276 explicit_file = os.environ.get(environment_vars.CREDENTIALS)
277
278 _LOGGER.debug(
279 "Checking %s for explicit credentials as part of auth process...", explicit_file
280 )
281
282 if explicit_file is not None and explicit_file == cloud_sdk_adc_path:
283 # Cloud sdk flow calls gcloud to fetch project id, so if the explicit
284 # file path is cloud sdk credentials path, then we should fall back
285 # to cloud sdk flow, otherwise project id cannot be obtained.
286 _LOGGER.debug(
287 "Explicit credentials path %s is the same as Cloud SDK credentials path, fall back to Cloud SDK credentials flow...",
288 explicit_file,
289 )
290 return _get_gcloud_sdk_credentials(quota_project_id=quota_project_id)
291
292 if explicit_file is not None:
293 credentials, project_id = load_credentials_from_file(
294 os.environ[environment_vars.CREDENTIALS], quota_project_id=quota_project_id
295 )
296 credentials._cred_file_path = f"{explicit_file} file via the GOOGLE_APPLICATION_CREDENTIALS environment variable"
297
298 return credentials, project_id
299
300 else:
301 return None, None
302
303
304def _get_gae_credentials():
305 """Gets Google App Engine App Identity credentials and project ID."""
306 # If not GAE gen1, prefer the metadata service even if the GAE APIs are
307 # available as per https://google.aip.dev/auth/4115.
308 if os.environ.get(environment_vars.LEGACY_APPENGINE_RUNTIME) != "python27":
309 return None, None
310
311 # While this library is normally bundled with app_engine, there are
312 # some cases where it's not available, so we tolerate ImportError.
313 try:
314 _LOGGER.debug("Checking for App Engine runtime as part of auth process...")
315 import google.auth.app_engine as app_engine
316 except ImportError:
317 _LOGGER.warning("Import of App Engine auth library failed.")
318 return None, None
319
320 try:
321 credentials = app_engine.Credentials()
322 project_id = app_engine.get_project_id()
323 return credentials, project_id
324 except EnvironmentError:
325 _LOGGER.debug(
326 "No App Engine library was found so cannot authentication via App Engine Identity Credentials."
327 )
328 return None, None
329
330
331def _get_gce_credentials(request=None, quota_project_id=None):
332 """Gets credentials and project ID from the GCE Metadata Service."""
333 # Ping requires a transport, but we want application default credentials
334 # to require no arguments. So, we'll use the _http_client transport which
335 # uses http.client. This is only acceptable because the metadata server
336 # doesn't do SSL and never requires proxies.
337
338 # While this library is normally bundled with compute_engine, there are
339 # some cases where it's not available, so we tolerate ImportError.
340 try:
341 from google.auth import compute_engine
342 from google.auth.compute_engine import _metadata
343 except ImportError:
344 _LOGGER.warning("Import of Compute Engine auth library failed.")
345 return None, None
346
347 if request is None:
348 request = google.auth.transport._http_client.Request()
349
350 if _metadata.is_on_gce(request=request):
351 # Get the project ID.
352 try:
353 project_id = _metadata.get_project_id(request=request)
354 except exceptions.TransportError:
355 project_id = None
356
357 cred = compute_engine.Credentials()
358 cred = _apply_quota_project_id(cred, quota_project_id)
359
360 return cred, project_id
361 else:
362 _LOGGER.warning(
363 "Authentication failed using Compute Engine authentication due to unavailable metadata server."
364 )
365 return None, None
366
367
368def _get_external_account_credentials(
369 info, filename, scopes=None, default_scopes=None, request=None
370):
371 """Loads external account Credentials from the parsed external account info.
372
373 The credentials information must correspond to a supported external account
374 credentials.
375
376 Args:
377 info (Mapping[str, str]): The external account info in Google format.
378 filename (str): The full path to the credentials file.
379 scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If
380 specified, the credentials will automatically be scoped if
381 necessary.
382 default_scopes (Optional[Sequence[str]]): Default scopes passed by a
383 Google client library. Use 'scopes' for user-defined scopes.
384 request (Optional[google.auth.transport.Request]): An object used to make
385 HTTP requests. This is used to determine the associated project ID
386 for a workload identity pool resource (external account credentials).
387 If not specified, then it will use a
388 google.auth.transport.requests.Request client to make requests.
389
390 Returns:
391 Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded
392 credentials and the project ID. External account credentials project
393 IDs may not always be determined.
394
395 Raises:
396 google.auth.exceptions.DefaultCredentialsError: if the info dictionary
397 is in the wrong format or is missing required information.
398 """
399 # There are currently 3 types of external_account credentials.
400 if info.get("subject_token_type") == _AWS_SUBJECT_TOKEN_TYPE:
401 # Check if configuration corresponds to an AWS credentials.
402 from google.auth import aws
403
404 credentials = aws.Credentials.from_info(
405 info, scopes=scopes, default_scopes=default_scopes
406 )
407 elif (
408 info.get("credential_source") is not None
409 and info.get("credential_source").get("executable") is not None
410 ):
411 from google.auth import pluggable
412
413 credentials = pluggable.Credentials.from_info(
414 info, scopes=scopes, default_scopes=default_scopes
415 )
416 else:
417 try:
418 # Check if configuration corresponds to an Identity Pool credentials.
419 from google.auth import identity_pool
420
421 credentials = identity_pool.Credentials.from_info(
422 info, scopes=scopes, default_scopes=default_scopes
423 )
424 except ValueError:
425 # If the configuration is invalid or does not correspond to any
426 # supported external_account credentials, raise an error.
427 raise exceptions.DefaultCredentialsError(
428 "Failed to load external account credentials from {}".format(filename)
429 )
430 if request is None:
431 import google.auth.transport.requests
432
433 request = google.auth.transport.requests.Request()
434
435 return credentials, credentials.get_project_id(request=request)
436
437
438def _get_external_account_authorized_user_credentials(
439 filename, info, scopes=None, default_scopes=None, request=None
440):
441 try:
442 from google.auth import external_account_authorized_user
443
444 credentials = external_account_authorized_user.Credentials.from_info(info)
445 except ValueError:
446 raise exceptions.DefaultCredentialsError(
447 "Failed to load external account authorized user credentials from {}".format(
448 filename
449 )
450 )
451
452 return credentials, None
453
454
455def _get_authorized_user_credentials(filename, info, scopes=None):
456 from google.oauth2 import credentials
457
458 try:
459 credentials = credentials.Credentials.from_authorized_user_info(
460 info, scopes=scopes
461 )
462 except ValueError as caught_exc:
463 msg = "Failed to load authorized user credentials from {}".format(filename)
464 new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
465 raise new_exc from caught_exc
466 return credentials, None
467
468
469def _get_service_account_credentials(filename, info, scopes=None, default_scopes=None):
470 from google.oauth2 import service_account
471
472 try:
473 credentials = service_account.Credentials.from_service_account_info(
474 info, scopes=scopes, default_scopes=default_scopes
475 )
476 except ValueError as caught_exc:
477 msg = "Failed to load service account credentials from {}".format(filename)
478 new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
479 raise new_exc from caught_exc
480 return credentials, info.get("project_id")
481
482
483def _get_impersonated_service_account_credentials(filename, info, scopes):
484 from google.auth import impersonated_credentials
485
486 try:
487 credentials = impersonated_credentials.Credentials.from_impersonated_service_account_info(
488 info, scopes=scopes
489 )
490 except ValueError as caught_exc:
491 msg = "Failed to load impersonated service account credentials from {}".format(
492 filename
493 )
494 new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
495 raise new_exc from caught_exc
496 return credentials, None
497
498
499def _get_gdch_service_account_credentials(filename, info):
500 from google.oauth2 import gdch_credentials
501
502 try:
503 credentials = gdch_credentials.ServiceAccountCredentials.from_service_account_info(
504 info
505 )
506 except ValueError as caught_exc:
507 msg = "Failed to load GDCH service account credentials from {}".format(filename)
508 new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
509 raise new_exc from caught_exc
510 return credentials, info.get("project")
511
512
513def get_api_key_credentials(key):
514 """Return credentials with the given API key."""
515 from google.auth import api_key
516
517 return api_key.Credentials(key)
518
519
520def _apply_quota_project_id(credentials, quota_project_id):
521 if quota_project_id:
522 credentials = credentials.with_quota_project(quota_project_id)
523 else:
524 credentials = credentials.with_quota_project_from_environment()
525
526 from google.oauth2 import credentials as authorized_user_credentials
527
528 if isinstance(credentials, authorized_user_credentials.Credentials) and (
529 not credentials.quota_project_id
530 ):
531 _warn_about_problematic_credentials(credentials)
532 return credentials
533
534
535def default(scopes=None, request=None, quota_project_id=None, default_scopes=None):
536 """Gets the default credentials for the current environment.
537
538 `Application Default Credentials`_ provides an easy way to obtain
539 credentials to call Google APIs for server-to-server or local applications.
540 This function acquires credentials from the environment in the following
541 order:
542
543 1. If the environment variable ``GOOGLE_APPLICATION_CREDENTIALS`` is set
544 to the path of a valid service account JSON private key file, then it is
545 loaded and returned. The project ID returned is the project ID defined
546 in the service account file if available (some older files do not
547 contain project ID information).
548
549 If the environment variable is set to the path of a valid external
550 account JSON configuration file (workload identity federation), then the
551 configuration file is used to determine and retrieve the external
552 credentials from the current environment (AWS, Azure, etc).
553 These will then be exchanged for Google access tokens via the Google STS
554 endpoint.
555 The project ID returned in this case is the one corresponding to the
556 underlying workload identity pool resource if determinable.
557
558 If the environment variable is set to the path of a valid GDCH service
559 account JSON file (`Google Distributed Cloud Hosted`_), then a GDCH
560 credential will be returned. The project ID returned is the project
561 specified in the JSON file.
562 2. If the `Google Cloud SDK`_ is installed and has application default
563 credentials set they are loaded and returned.
564
565 To enable application default credentials with the Cloud SDK run::
566
567 gcloud auth application-default login
568
569 If the Cloud SDK has an active project, the project ID is returned. The
570 active project can be set using::
571
572 gcloud config set project
573
574 3. If the application is running in the `App Engine standard environment`_
575 (first generation) then the credentials and project ID from the
576 `App Identity Service`_ are used.
577 4. If the application is running in `Compute Engine`_ or `Cloud Run`_ or
578 the `App Engine flexible environment`_ or the `App Engine standard
579 environment`_ (second generation) then the credentials and project ID
580 are obtained from the `Metadata Service`_.
581 5. If no credentials are found,
582 :class:`~google.auth.exceptions.DefaultCredentialsError` will be raised.
583
584 .. _Application Default Credentials: https://developers.google.com\
585 /identity/protocols/application-default-credentials
586 .. _Google Cloud SDK: https://cloud.google.com/sdk
587 .. _App Engine standard environment: https://cloud.google.com/appengine
588 .. _App Identity Service: https://cloud.google.com/appengine/docs/python\
589 /appidentity/
590 .. _Compute Engine: https://cloud.google.com/compute
591 .. _App Engine flexible environment: https://cloud.google.com\
592 /appengine/flexible
593 .. _Metadata Service: https://cloud.google.com/compute/docs\
594 /storing-retrieving-metadata
595 .. _Cloud Run: https://cloud.google.com/run
596 .. _Google Distributed Cloud Hosted: https://cloud.google.com/blog/topics\
597 /hybrid-cloud/announcing-google-distributed-cloud-edge-and-hosted
598
599 Example::
600
601 import google.auth
602
603 credentials, project_id = google.auth.default()
604
605 Args:
606 scopes (Sequence[str]): The list of scopes for the credentials. If
607 specified, the credentials will automatically be scoped if
608 necessary.
609 request (Optional[google.auth.transport.Request]): An object used to make
610 HTTP requests. This is used to either detect whether the application
611 is running on Compute Engine or to determine the associated project
612 ID for a workload identity pool resource (external account
613 credentials). If not specified, then it will either use the standard
614 library http client to make requests for Compute Engine credentials
615 or a google.auth.transport.requests.Request client for external
616 account credentials.
617 quota_project_id (Optional[str]): The project ID used for
618 quota and billing.
619 default_scopes (Optional[Sequence[str]]): Default scopes passed by a
620 Google client library. Use 'scopes' for user-defined scopes.
621 Returns:
622 Tuple[~google.auth.credentials.Credentials, Optional[str]]:
623 the current environment's credentials and project ID. Project ID
624 may be None, which indicates that the Project ID could not be
625 ascertained from the environment.
626
627 Raises:
628 ~google.auth.exceptions.DefaultCredentialsError:
629 If no credentials were found, or if the credentials found were
630 invalid.
631 """
632 from google.auth.credentials import with_scopes_if_required
633 from google.auth.credentials import CredentialsWithQuotaProject
634
635 explicit_project_id = os.environ.get(
636 environment_vars.PROJECT, os.environ.get(environment_vars.LEGACY_PROJECT)
637 )
638
639 checkers = (
640 # Avoid passing scopes here to prevent passing scopes to user credentials.
641 # with_scopes_if_required() below will ensure scopes/default scopes are
642 # safely set on the returned credentials since requires_scopes will
643 # guard against setting scopes on user credentials.
644 lambda: _get_explicit_environ_credentials(quota_project_id=quota_project_id),
645 lambda: _get_gcloud_sdk_credentials(quota_project_id=quota_project_id),
646 _get_gae_credentials,
647 lambda: _get_gce_credentials(request, quota_project_id=quota_project_id),
648 )
649
650 for checker in checkers:
651 credentials, project_id = checker()
652 if credentials is not None:
653 credentials = with_scopes_if_required(
654 credentials, scopes, default_scopes=default_scopes
655 )
656
657 effective_project_id = explicit_project_id or project_id
658
659 # For external account credentials, scopes are required to determine
660 # the project ID. Try to get the project ID again if not yet
661 # determined.
662 if not effective_project_id and callable(
663 getattr(credentials, "get_project_id", None)
664 ):
665 if request is None:
666 import google.auth.transport.requests
667
668 request = google.auth.transport.requests.Request()
669 effective_project_id = credentials.get_project_id(request=request)
670
671 if quota_project_id and isinstance(
672 credentials, CredentialsWithQuotaProject
673 ):
674 credentials = credentials.with_quota_project(quota_project_id)
675
676 if not effective_project_id:
677 _LOGGER.warning(
678 "No project ID could be determined. Consider running "
679 "`gcloud config set project` or setting the %s "
680 "environment variable",
681 environment_vars.PROJECT,
682 )
683 return credentials, effective_project_id
684
685 raise exceptions.DefaultCredentialsError(_CLOUD_SDK_MISSING_CREDENTIALS)