blob: d4ccbc6ec530a09f7da5b630eaf93f20e6bfc2b6 [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
arithmetic172811ebaeb2021-09-07 14:38:49 -0700175def _get_gcloud_sdk_credentials(quota_project_id=None):
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700176 """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
arithmetic172811ebaeb2021-09-07 14:38:49 -0700188 credentials, project_id = load_credentials_from_file(
189 credentials_filename, quota_project_id=quota_project_id
190 )
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
arithmetic172811ebaeb2021-09-07 14:38:49 -0700198def _get_explicit_environ_credentials(quota_project_id=None):
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."""
arithmetic1728333cb762021-02-25 15:42:32 -0800201 from google.auth import _cloud_sdk
202
203 cloud_sdk_adc_path = _cloud_sdk.get_application_default_credentials_path()
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700204 explicit_file = os.environ.get(environment_vars.CREDENTIALS)
205
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400206 _LOGGER.debug(
207 "Checking %s for explicit credentials as part of auth process...", explicit_file
208 )
209
arithmetic1728333cb762021-02-25 15:42:32 -0800210 if explicit_file is not None and explicit_file == cloud_sdk_adc_path:
211 # Cloud sdk flow calls gcloud to fetch project id, so if the explicit
212 # file path is cloud sdk credentials path, then we should fall back
213 # to cloud sdk flow, otherwise project id cannot be obtained.
214 _LOGGER.debug(
215 "Explicit credentials path %s is the same as Cloud SDK credentials path, fall back to Cloud SDK credentials flow...",
216 explicit_file,
217 )
arithmetic172811ebaeb2021-09-07 14:38:49 -0700218 return _get_gcloud_sdk_credentials(quota_project_id=quota_project_id)
arithmetic1728333cb762021-02-25 15:42:32 -0800219
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700220 if explicit_file is not None:
Bu Sun Kim15d5fa92020-06-18 14:05:40 -0700221 credentials, project_id = load_credentials_from_file(
arithmetic172811ebaeb2021-09-07 14:38:49 -0700222 os.environ[environment_vars.CREDENTIALS], quota_project_id=quota_project_id
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700223 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700224
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700225 return credentials, project_id
226
227 else:
228 return None, None
229
230
231def _get_gae_credentials():
232 """Gets Google App Engine App Identity credentials and project ID."""
Zev Goldstein7f7d92d2021-07-23 15:52:46 -0400233 # If not GAE gen1, prefer the metadata service even if the GAE APIs are
234 # available as per https://google.aip.dev/auth/4115.
235 if os.environ.get(environment_vars.LEGACY_APPENGINE_RUNTIME) != "python27":
236 return None, None
237
James Wilson6e0781b2018-12-20 20:38:52 -0500238 # While this library is normally bundled with app_engine, there are
239 # some cases where it's not available, so we tolerate ImportError.
240 try:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400241 _LOGGER.debug("Checking for App Engine runtime as part of auth process...")
James Wilson6e0781b2018-12-20 20:38:52 -0500242 import google.auth.app_engine as app_engine
243 except ImportError:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400244 _LOGGER.warning("Import of App Engine auth library failed.")
James Wilson6e0781b2018-12-20 20:38:52 -0500245 return None, None
Jon Wayne Parrott6dca98c2016-12-01 15:34:59 -0800246
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -0700247 try:
248 credentials = app_engine.Credentials()
249 project_id = app_engine.get_project_id()
250 return credentials, project_id
251 except EnvironmentError:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400252 _LOGGER.debug(
253 "No App Engine library was found so cannot authentication via App Engine Identity Credentials."
254 )
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -0700255 return None, None
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700256
257
258def _get_gce_credentials(request=None):
259 """Gets credentials and project ID from the GCE Metadata Service."""
260 # Ping requires a transport, but we want application default credentials
261 # to require no arguments. So, we'll use the _http_client transport which
262 # uses http.client. This is only acceptable because the metadata server
263 # doesn't do SSL and never requires proxies.
James Wilson6e0781b2018-12-20 20:38:52 -0500264
265 # While this library is normally bundled with compute_engine, there are
266 # some cases where it's not available, so we tolerate ImportError.
267 try:
268 from google.auth import compute_engine
269 from google.auth.compute_engine import _metadata
270 except ImportError:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400271 _LOGGER.warning("Import of Compute Engine auth library failed.")
James Wilson6e0781b2018-12-20 20:38:52 -0500272 return None, None
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700273
274 if request is None:
275 request = google.auth.transport._http_client.Request()
276
277 if _metadata.ping(request=request):
278 # Get the project ID.
279 try:
Jon Wayne Parrott5b03ba12016-10-24 13:51:26 -0700280 project_id = _metadata.get_project_id(request=request)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700281 except exceptions.TransportError:
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700282 project_id = None
283
284 return compute_engine.Credentials(), project_id
285 else:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400286 _LOGGER.warning(
287 "Authentication failed using Compute Engine authentication due to unavailable metadata server."
288 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700289 return None, None
290
291
bojeil-googled4d7f382021-02-16 12:33:20 -0800292def _get_external_account_credentials(
293 info, filename, scopes=None, default_scopes=None, request=None
294):
295 """Loads external account Credentials from the parsed external account info.
296
297 The credentials information must correspond to a supported external account
298 credentials.
299
300 Args:
301 info (Mapping[str, str]): The external account info in Google format.
302 filename (str): The full path to the credentials file.
303 scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If
304 specified, the credentials will automatically be scoped if
305 necessary.
306 default_scopes (Optional[Sequence[str]]): Default scopes passed by a
307 Google client library. Use 'scopes' for user-defined scopes.
308 request (Optional[google.auth.transport.Request]): An object used to make
309 HTTP requests. This is used to determine the associated project ID
310 for a workload identity pool resource (external account credentials).
311 If not specified, then it will use a
312 google.auth.transport.requests.Request client to make requests.
313
314 Returns:
315 Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded
316 credentials and the project ID. External account credentials project
317 IDs may not always be determined.
318
319 Raises:
320 google.auth.exceptions.DefaultCredentialsError: if the info dictionary
321 is in the wrong format or is missing required information.
322 """
323 # There are currently 2 types of external_account credentials.
324 try:
325 # Check if configuration corresponds to an AWS credentials.
326 from google.auth import aws
327
328 credentials = aws.Credentials.from_info(
329 info, scopes=scopes, default_scopes=default_scopes
330 )
331 except ValueError:
332 try:
333 # Check if configuration corresponds to an Identity Pool credentials.
334 from google.auth import identity_pool
335
336 credentials = identity_pool.Credentials.from_info(
337 info, scopes=scopes, default_scopes=default_scopes
338 )
339 except ValueError:
340 # If the configuration is invalid or does not correspond to any
341 # supported external_account credentials, raise an error.
342 raise exceptions.DefaultCredentialsError(
343 "Failed to load external account credentials from {}".format(filename)
344 )
345 if request is None:
346 request = google.auth.transport.requests.Request()
347
348 return credentials, credentials.get_project_id(request=request)
349
350
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700351def default(scopes=None, request=None, quota_project_id=None, default_scopes=None):
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700352 """Gets the default credentials for the current environment.
353
354 `Application Default Credentials`_ provides an easy way to obtain
355 credentials to call Google APIs for server-to-server or local applications.
356 This function acquires credentials from the environment in the following
357 order:
358
359 1. If the environment variable ``GOOGLE_APPLICATION_CREDENTIALS`` is set
360 to the path of a valid service account JSON private key file, then it is
361 loaded and returned. The project ID returned is the project ID defined
362 in the service account file if available (some older files do not
363 contain project ID information).
bojeil-googled4d7f382021-02-16 12:33:20 -0800364
365 If the environment variable is set to the path of a valid external
366 account JSON configuration file (workload identity federation), then the
367 configuration file is used to determine and retrieve the external
368 credentials from the current environment (AWS, Azure, etc).
369 These will then be exchanged for Google access tokens via the Google STS
370 endpoint.
371 The project ID returned in this case is the one corresponding to the
372 underlying workload identity pool resource if determinable.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700373 2. If the `Google Cloud SDK`_ is installed and has application default
374 credentials set they are loaded and returned.
375
376 To enable application default credentials with the Cloud SDK run::
377
378 gcloud auth application-default login
379
380 If the Cloud SDK has an active project, the project ID is returned. The
381 active project can be set using::
382
383 gcloud config set project
384
385 3. If the application is running in the `App Engine standard environment`_
David Buxton0323cf32020-10-29 21:26:11 +0000386 (first generation) then the credentials and project ID from the
387 `App Identity Service`_ are used.
388 4. If the application is running in `Compute Engine`_ or `Cloud Run`_ or
389 the `App Engine flexible environment`_ or the `App Engine standard
390 environment`_ (second generation) then the credentials and project ID
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700391 are obtained from the `Metadata Service`_.
392 5. If no credentials are found,
393 :class:`~google.auth.exceptions.DefaultCredentialsError` will be raised.
394
395 .. _Application Default Credentials: https://developers.google.com\
396 /identity/protocols/application-default-credentials
397 .. _Google Cloud SDK: https://cloud.google.com/sdk
398 .. _App Engine standard environment: https://cloud.google.com/appengine
399 .. _App Identity Service: https://cloud.google.com/appengine/docs/python\
400 /appidentity/
401 .. _Compute Engine: https://cloud.google.com/compute
402 .. _App Engine flexible environment: https://cloud.google.com\
403 /appengine/flexible
404 .. _Metadata Service: https://cloud.google.com/compute/docs\
405 /storing-retrieving-metadata
David Buxton0323cf32020-10-29 21:26:11 +0000406 .. _Cloud Run: https://cloud.google.com/run
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700407
408 Example::
409
410 import google.auth
411
412 credentials, project_id = google.auth.default()
413
414 Args:
Jon Wayne Parrott8a7e5062016-11-07 16:45:17 -0800415 scopes (Sequence[str]): The list of scopes for the credentials. If
416 specified, the credentials will automatically be scoped if
417 necessary.
bojeil-googled4d7f382021-02-16 12:33:20 -0800418 request (Optional[google.auth.transport.Request]): An object used to make
419 HTTP requests. This is used to either detect whether the application
420 is running on Compute Engine or to determine the associated project
421 ID for a workload identity pool resource (external account
422 credentials). If not specified, then it will either use the standard
423 library http client to make requests for Compute Engine credentials
424 or a google.auth.transport.requests.Request client for external
425 account credentials.
426 quota_project_id (Optional[str]): The project ID used for
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700427 quota and billing.
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700428 default_scopes (Optional[Sequence[str]]): Default scopes passed by a
429 Google client library. Use 'scopes' for user-defined scopes.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700430 Returns:
431 Tuple[~google.auth.credentials.Credentials, Optional[str]]:
432 the current environment's credentials and project ID. Project ID
433 may be None, which indicates that the Project ID could not be
434 ascertained from the environment.
435
436 Raises:
437 ~google.auth.exceptions.DefaultCredentialsError:
438 If no credentials were found, or if the credentials found were
439 invalid.
440 """
Jon Wayne Parrott6dca98c2016-12-01 15:34:59 -0800441 from google.auth.credentials import with_scopes_if_required
442
Jon Wayne Parrottce37cba2016-11-07 16:41:42 -0800443 explicit_project_id = os.environ.get(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700444 environment_vars.PROJECT, os.environ.get(environment_vars.LEGACY_PROJECT)
445 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700446
447 checkers = (
bojeil-googled4d7f382021-02-16 12:33:20 -0800448 # Avoid passing scopes here to prevent passing scopes to user credentials.
449 # with_scopes_if_required() below will ensure scopes/default scopes are
450 # safely set on the returned credentials since requires_scopes will
451 # guard against setting scopes on user credentials.
arithmetic172811ebaeb2021-09-07 14:38:49 -0700452 lambda: _get_explicit_environ_credentials(quota_project_id=quota_project_id),
453 lambda: _get_gcloud_sdk_credentials(quota_project_id=quota_project_id),
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700454 _get_gae_credentials,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700455 lambda: _get_gce_credentials(request),
456 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700457
458 for checker in checkers:
459 credentials, project_id = checker()
460 if credentials is not None:
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700461 credentials = with_scopes_if_required(
462 credentials, scopes, default_scopes=default_scopes
463 )
bojeil-googled4d7f382021-02-16 12:33:20 -0800464
465 # For external account credentials, scopes are required to determine
466 # the project ID. Try to get the project ID again if not yet
467 # determined.
468 if not project_id and callable(
469 getattr(credentials, "get_project_id", None)
470 ):
471 if request is None:
472 request = google.auth.transport.requests.Request()
473 project_id = credentials.get_project_id(request=request)
474
Bu Sun Kimab2be5d2020-07-15 16:49:27 -0700475 if quota_project_id:
476 credentials = credentials.with_quota_project(quota_project_id)
477
Jacob Hayes15af07b2017-12-13 14:09:47 -0600478 effective_project_id = explicit_project_id or project_id
479 if not effective_project_id:
480 _LOGGER.warning(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700481 "No project ID could be determined. Consider running "
482 "`gcloud config set project` or setting the %s "
483 "environment variable",
484 environment_vars.PROJECT,
485 )
Jacob Hayes15af07b2017-12-13 14:09:47 -0600486 return credentials, effective_project_id
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700487
488 raise exceptions.DefaultCredentialsError(_HELP_MESSAGE)