blob: 4dc0725e7f03a6719220bf9b121102b37a60735d [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."""
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 )
218 return _get_gcloud_sdk_credentials()
219
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(
Bu Sun Kim9dc2d862021-02-11 12:53:26 -0700222 os.environ[environment_vars.CREDENTIALS]
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."""
James Wilson6e0781b2018-12-20 20:38:52 -0500233 # While this library is normally bundled with app_engine, there are
234 # some cases where it's not available, so we tolerate ImportError.
235 try:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400236 _LOGGER.debug("Checking for App Engine runtime as part of auth process...")
James Wilson6e0781b2018-12-20 20:38:52 -0500237 import google.auth.app_engine as app_engine
238 except ImportError:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400239 _LOGGER.warning("Import of App Engine auth library failed.")
James Wilson6e0781b2018-12-20 20:38:52 -0500240 return None, None
Jon Wayne Parrott6dca98c2016-12-01 15:34:59 -0800241
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -0700242 try:
243 credentials = app_engine.Credentials()
244 project_id = app_engine.get_project_id()
245 return credentials, project_id
246 except EnvironmentError:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400247 _LOGGER.debug(
248 "No App Engine library was found so cannot authentication via App Engine Identity Credentials."
249 )
Jon Wayne Parrott2148fde2016-10-24 13:44:25 -0700250 return None, None
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700251
252
253def _get_gce_credentials(request=None):
254 """Gets credentials and project ID from the GCE Metadata Service."""
255 # Ping requires a transport, but we want application default credentials
256 # to require no arguments. So, we'll use the _http_client transport which
257 # uses http.client. This is only acceptable because the metadata server
258 # doesn't do SSL and never requires proxies.
James Wilson6e0781b2018-12-20 20:38:52 -0500259
260 # While this library is normally bundled with compute_engine, there are
261 # some cases where it's not available, so we tolerate ImportError.
262 try:
263 from google.auth import compute_engine
264 from google.auth.compute_engine import _metadata
265 except ImportError:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400266 _LOGGER.warning("Import of Compute Engine auth library failed.")
James Wilson6e0781b2018-12-20 20:38:52 -0500267 return None, None
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700268
269 if request is None:
270 request = google.auth.transport._http_client.Request()
271
272 if _metadata.ping(request=request):
273 # Get the project ID.
274 try:
Jon Wayne Parrott5b03ba12016-10-24 13:51:26 -0700275 project_id = _metadata.get_project_id(request=request)
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700276 except exceptions.TransportError:
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700277 project_id = None
278
279 return compute_engine.Credentials(), project_id
280 else:
Vaughan Hiltsecd88d42020-07-21 16:25:51 -0400281 _LOGGER.warning(
282 "Authentication failed using Compute Engine authentication due to unavailable metadata server."
283 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700284 return None, None
285
286
bojeil-googled4d7f382021-02-16 12:33:20 -0800287def _get_external_account_credentials(
288 info, filename, scopes=None, default_scopes=None, request=None
289):
290 """Loads external account Credentials from the parsed external account info.
291
292 The credentials information must correspond to a supported external account
293 credentials.
294
295 Args:
296 info (Mapping[str, str]): The external account info in Google format.
297 filename (str): The full path to the credentials file.
298 scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If
299 specified, the credentials will automatically be scoped if
300 necessary.
301 default_scopes (Optional[Sequence[str]]): Default scopes passed by a
302 Google client library. Use 'scopes' for user-defined scopes.
303 request (Optional[google.auth.transport.Request]): An object used to make
304 HTTP requests. This is used to determine the associated project ID
305 for a workload identity pool resource (external account credentials).
306 If not specified, then it will use a
307 google.auth.transport.requests.Request client to make requests.
308
309 Returns:
310 Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded
311 credentials and the project ID. External account credentials project
312 IDs may not always be determined.
313
314 Raises:
315 google.auth.exceptions.DefaultCredentialsError: if the info dictionary
316 is in the wrong format or is missing required information.
317 """
318 # There are currently 2 types of external_account credentials.
319 try:
320 # Check if configuration corresponds to an AWS credentials.
321 from google.auth import aws
322
323 credentials = aws.Credentials.from_info(
324 info, scopes=scopes, default_scopes=default_scopes
325 )
326 except ValueError:
327 try:
328 # Check if configuration corresponds to an Identity Pool credentials.
329 from google.auth import identity_pool
330
331 credentials = identity_pool.Credentials.from_info(
332 info, scopes=scopes, default_scopes=default_scopes
333 )
334 except ValueError:
335 # If the configuration is invalid or does not correspond to any
336 # supported external_account credentials, raise an error.
337 raise exceptions.DefaultCredentialsError(
338 "Failed to load external account credentials from {}".format(filename)
339 )
340 if request is None:
341 request = google.auth.transport.requests.Request()
342
343 return credentials, credentials.get_project_id(request=request)
344
345
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700346def default(scopes=None, request=None, quota_project_id=None, default_scopes=None):
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700347 """Gets the default credentials for the current environment.
348
349 `Application Default Credentials`_ provides an easy way to obtain
350 credentials to call Google APIs for server-to-server or local applications.
351 This function acquires credentials from the environment in the following
352 order:
353
354 1. If the environment variable ``GOOGLE_APPLICATION_CREDENTIALS`` is set
355 to the path of a valid service account JSON private key file, then it is
356 loaded and returned. The project ID returned is the project ID defined
357 in the service account file if available (some older files do not
358 contain project ID information).
bojeil-googled4d7f382021-02-16 12:33:20 -0800359
360 If the environment variable is set to the path of a valid external
361 account JSON configuration file (workload identity federation), then the
362 configuration file is used to determine and retrieve the external
363 credentials from the current environment (AWS, Azure, etc).
364 These will then be exchanged for Google access tokens via the Google STS
365 endpoint.
366 The project ID returned in this case is the one corresponding to the
367 underlying workload identity pool resource if determinable.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700368 2. If the `Google Cloud SDK`_ is installed and has application default
369 credentials set they are loaded and returned.
370
371 To enable application default credentials with the Cloud SDK run::
372
373 gcloud auth application-default login
374
375 If the Cloud SDK has an active project, the project ID is returned. The
376 active project can be set using::
377
378 gcloud config set project
379
380 3. If the application is running in the `App Engine standard environment`_
David Buxton0323cf32020-10-29 21:26:11 +0000381 (first generation) then the credentials and project ID from the
382 `App Identity Service`_ are used.
383 4. If the application is running in `Compute Engine`_ or `Cloud Run`_ or
384 the `App Engine flexible environment`_ or the `App Engine standard
385 environment`_ (second generation) then the credentials and project ID
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700386 are obtained from the `Metadata Service`_.
387 5. If no credentials are found,
388 :class:`~google.auth.exceptions.DefaultCredentialsError` will be raised.
389
390 .. _Application Default Credentials: https://developers.google.com\
391 /identity/protocols/application-default-credentials
392 .. _Google Cloud SDK: https://cloud.google.com/sdk
393 .. _App Engine standard environment: https://cloud.google.com/appengine
394 .. _App Identity Service: https://cloud.google.com/appengine/docs/python\
395 /appidentity/
396 .. _Compute Engine: https://cloud.google.com/compute
397 .. _App Engine flexible environment: https://cloud.google.com\
398 /appengine/flexible
399 .. _Metadata Service: https://cloud.google.com/compute/docs\
400 /storing-retrieving-metadata
David Buxton0323cf32020-10-29 21:26:11 +0000401 .. _Cloud Run: https://cloud.google.com/run
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700402
403 Example::
404
405 import google.auth
406
407 credentials, project_id = google.auth.default()
408
409 Args:
Jon Wayne Parrott8a7e5062016-11-07 16:45:17 -0800410 scopes (Sequence[str]): The list of scopes for the credentials. If
411 specified, the credentials will automatically be scoped if
412 necessary.
bojeil-googled4d7f382021-02-16 12:33:20 -0800413 request (Optional[google.auth.transport.Request]): An object used to make
414 HTTP requests. This is used to either detect whether the application
415 is running on Compute Engine or to determine the associated project
416 ID for a workload identity pool resource (external account
417 credentials). If not specified, then it will either use the standard
418 library http client to make requests for Compute Engine credentials
419 or a google.auth.transport.requests.Request client for external
420 account credentials.
421 quota_project_id (Optional[str]): The project ID used for
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700422 quota and billing.
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700423 default_scopes (Optional[Sequence[str]]): Default scopes passed by a
424 Google client library. Use 'scopes' for user-defined scopes.
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700425 Returns:
426 Tuple[~google.auth.credentials.Credentials, Optional[str]]:
427 the current environment's credentials and project ID. Project ID
428 may be None, which indicates that the Project ID could not be
429 ascertained from the environment.
430
431 Raises:
432 ~google.auth.exceptions.DefaultCredentialsError:
433 If no credentials were found, or if the credentials found were
434 invalid.
435 """
Jon Wayne Parrott6dca98c2016-12-01 15:34:59 -0800436 from google.auth.credentials import with_scopes_if_required
437
Jon Wayne Parrottce37cba2016-11-07 16:41:42 -0800438 explicit_project_id = os.environ.get(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700439 environment_vars.PROJECT, os.environ.get(environment_vars.LEGACY_PROJECT)
440 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700441
442 checkers = (
bojeil-googled4d7f382021-02-16 12:33:20 -0800443 # Avoid passing scopes here to prevent passing scopes to user credentials.
444 # with_scopes_if_required() below will ensure scopes/default scopes are
445 # safely set on the returned credentials since requires_scopes will
446 # guard against setting scopes on user credentials.
Bu Sun Kim9dc2d862021-02-11 12:53:26 -0700447 _get_explicit_environ_credentials,
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700448 _get_gcloud_sdk_credentials,
449 _get_gae_credentials,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700450 lambda: _get_gce_credentials(request),
451 )
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700452
453 for checker in checkers:
454 credentials, project_id = checker()
455 if credentials is not None:
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700456 credentials = with_scopes_if_required(
457 credentials, scopes, default_scopes=default_scopes
458 )
bojeil-googled4d7f382021-02-16 12:33:20 -0800459
460 # For external account credentials, scopes are required to determine
461 # the project ID. Try to get the project ID again if not yet
462 # determined.
463 if not project_id and callable(
464 getattr(credentials, "get_project_id", None)
465 ):
466 if request is None:
467 request = google.auth.transport.requests.Request()
468 project_id = credentials.get_project_id(request=request)
469
Bu Sun Kimab2be5d2020-07-15 16:49:27 -0700470 if quota_project_id:
471 credentials = credentials.with_quota_project(quota_project_id)
472
Jacob Hayes15af07b2017-12-13 14:09:47 -0600473 effective_project_id = explicit_project_id or project_id
474 if not effective_project_id:
475 _LOGGER.warning(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700476 "No project ID could be determined. Consider running "
477 "`gcloud config set project` or setting the %s "
478 "environment variable",
479 environment_vars.PROJECT,
480 )
Jacob Hayes15af07b2017-12-13 14:09:47 -0600481 return credentials, effective_project_id
Jon Wayne Parrottaadb3de2016-10-19 09:34:05 -0700482
483 raise exceptions.DefaultCredentialsError(_HELP_MESSAGE)