google-auth 2.46.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. google/auth/__init__.py +53 -0
  2. google/auth/_agent_identity_utils.py +281 -0
  3. google/auth/_cloud_sdk.py +153 -0
  4. google/auth/_constants.py +5 -0
  5. google/auth/_credentials_async.py +171 -0
  6. google/auth/_credentials_base.py +75 -0
  7. google/auth/_default.py +752 -0
  8. google/auth/_default_async.py +288 -0
  9. google/auth/_exponential_backoff.py +164 -0
  10. google/auth/_helpers.py +554 -0
  11. google/auth/_jwt_async.py +164 -0
  12. google/auth/_oauth2client.py +167 -0
  13. google/auth/_refresh_worker.py +109 -0
  14. google/auth/_service_account_info.py +80 -0
  15. google/auth/aio/__init__.py +25 -0
  16. google/auth/aio/_helpers.py +62 -0
  17. google/auth/aio/credentials.py +143 -0
  18. google/auth/aio/transport/__init__.py +144 -0
  19. google/auth/aio/transport/aiohttp.py +190 -0
  20. google/auth/aio/transport/sessions.py +268 -0
  21. google/auth/api_key.py +76 -0
  22. google/auth/app_engine.py +180 -0
  23. google/auth/aws.py +862 -0
  24. google/auth/compute_engine/__init__.py +22 -0
  25. google/auth/compute_engine/_metadata.py +487 -0
  26. google/auth/compute_engine/_mtls.py +164 -0
  27. google/auth/compute_engine/credentials.py +556 -0
  28. google/auth/credentials.py +665 -0
  29. google/auth/crypt/__init__.py +105 -0
  30. google/auth/crypt/_cryptography_rsa.py +151 -0
  31. google/auth/crypt/_helpers.py +0 -0
  32. google/auth/crypt/_python_rsa.py +175 -0
  33. google/auth/crypt/base.py +127 -0
  34. google/auth/crypt/es.py +221 -0
  35. google/auth/crypt/es256.py +45 -0
  36. google/auth/crypt/rsa.py +30 -0
  37. google/auth/downscoped.py +512 -0
  38. google/auth/environment_vars.py +103 -0
  39. google/auth/exceptions.py +108 -0
  40. google/auth/external_account.py +716 -0
  41. google/auth/external_account_authorized_user.py +458 -0
  42. google/auth/iam.py +136 -0
  43. google/auth/identity_pool.py +575 -0
  44. google/auth/impersonated_credentials.py +712 -0
  45. google/auth/jwt.py +878 -0
  46. google/auth/metrics.py +156 -0
  47. google/auth/pluggable.py +455 -0
  48. google/auth/py.typed +2 -0
  49. google/auth/transport/__init__.py +104 -0
  50. google/auth/transport/_aiohttp_requests.py +396 -0
  51. google/auth/transport/_custom_tls_signer.py +283 -0
  52. google/auth/transport/_http_client.py +114 -0
  53. google/auth/transport/_mtls_helper.py +503 -0
  54. google/auth/transport/_requests_base.py +53 -0
  55. google/auth/transport/grpc.py +337 -0
  56. google/auth/transport/mtls.py +137 -0
  57. google/auth/transport/requests.py +634 -0
  58. google/auth/transport/urllib3.py +493 -0
  59. google/auth/version.py +15 -0
  60. google/oauth2/__init__.py +36 -0
  61. google/oauth2/_client.py +631 -0
  62. google/oauth2/_client_async.py +290 -0
  63. google/oauth2/_credentials_async.py +118 -0
  64. google/oauth2/_id_token_async.py +287 -0
  65. google/oauth2/_reauth_async.py +330 -0
  66. google/oauth2/_service_account_async.py +132 -0
  67. google/oauth2/challenges.py +281 -0
  68. google/oauth2/credentials.py +617 -0
  69. google/oauth2/gdch_credentials.py +251 -0
  70. google/oauth2/id_token.py +372 -0
  71. google/oauth2/py.typed +2 -0
  72. google/oauth2/reauth.py +373 -0
  73. google/oauth2/service_account.py +880 -0
  74. google/oauth2/sts.py +201 -0
  75. google/oauth2/utils.py +168 -0
  76. google/oauth2/webauthn_handler.py +82 -0
  77. google/oauth2/webauthn_handler_factory.py +16 -0
  78. google/oauth2/webauthn_types.py +156 -0
  79. google_auth-2.46.0.dist-info/METADATA +184 -0
  80. google_auth-2.46.0.dist-info/RECORD +83 -0
  81. google_auth-2.46.0.dist-info/WHEEL +5 -0
  82. google_auth-2.46.0.dist-info/licenses/LICENSE +201 -0
  83. google_auth-2.46.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,53 @@
1
+ # Copyright 2016 Google LLC
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
+ """Google Auth Library for Python."""
16
+
17
+ import logging
18
+ import sys
19
+ import warnings
20
+
21
+ from google.auth import version as google_auth_version
22
+ from google.auth._default import (
23
+ default,
24
+ load_credentials_from_dict,
25
+ load_credentials_from_file,
26
+ )
27
+
28
+
29
+ __version__ = google_auth_version.__version__
30
+
31
+
32
+ __all__ = ["default", "load_credentials_from_file", "load_credentials_from_dict"]
33
+
34
+
35
+ class Python37DeprecationWarning(DeprecationWarning): # pragma: NO COVER
36
+ """
37
+ Deprecation warning raised when Python 3.7 runtime is detected.
38
+ Python 3.7 support will be dropped after January 1, 2024.
39
+ """
40
+
41
+ pass
42
+
43
+
44
+ # Checks if the current runtime is Python 3.7.
45
+ if sys.version_info.major == 3 and sys.version_info.minor == 7: # pragma: NO COVER
46
+ message = (
47
+ "After January 1, 2024, new releases of this library will drop support "
48
+ "for Python 3.7."
49
+ )
50
+ warnings.warn(message, Python37DeprecationWarning)
51
+
52
+ # Set default logging handler to avoid "No handler found" warnings.
53
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
@@ -0,0 +1,281 @@
1
+ # Copyright 2025 Google LLC
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
+ """Helpers for Agent Identity credentials."""
16
+
17
+ import base64
18
+ import hashlib
19
+ import logging
20
+ import os
21
+ import re
22
+ import time
23
+ from urllib.parse import quote, urlparse
24
+
25
+ from google.auth import environment_vars
26
+ from google.auth import exceptions
27
+ from google.auth.transport import _mtls_helper
28
+
29
+
30
+ _LOGGER = logging.getLogger(__name__)
31
+
32
+ CRYPTOGRAPHY_NOT_FOUND_ERROR = (
33
+ "The cryptography library is required for certificate-based authentication."
34
+ "Please install it with `pip install google-auth[cryptography]`."
35
+ )
36
+
37
+ # SPIFFE trust domain patterns for Agent Identities.
38
+ _AGENT_IDENTITY_SPIFFE_TRUST_DOMAIN_PATTERNS = [
39
+ r"^agents\.global\.org-\d+\.system\.id\.goog$",
40
+ r"^agents\.global\.proj-\d+\.system\.id\.goog$",
41
+ ]
42
+
43
+ _WELL_KNOWN_CERT_PATH = "/var/run/secrets/workload-spiffe-credentials/certificates.pem"
44
+
45
+ # Constants for polling the certificate file.
46
+ _FAST_POLL_CYCLES = 50
47
+ _FAST_POLL_INTERVAL = 0.1 # 100ms
48
+ _SLOW_POLL_INTERVAL = 0.5 # 500ms
49
+ _TOTAL_TIMEOUT = 30 # seconds
50
+
51
+ # Calculate the number of slow poll cycles based on the total timeout.
52
+ _SLOW_POLL_CYCLES = int(
53
+ (_TOTAL_TIMEOUT - (_FAST_POLL_CYCLES * _FAST_POLL_INTERVAL)) / _SLOW_POLL_INTERVAL
54
+ )
55
+
56
+ _POLLING_INTERVALS = ([_FAST_POLL_INTERVAL] * _FAST_POLL_CYCLES) + (
57
+ [_SLOW_POLL_INTERVAL] * _SLOW_POLL_CYCLES
58
+ )
59
+
60
+
61
+ def _is_certificate_file_ready(path):
62
+ """Checks if a file exists and is not empty."""
63
+ return path and os.path.exists(path) and os.path.getsize(path) > 0
64
+
65
+
66
+ def get_agent_identity_certificate_path():
67
+ """Gets the certificate path from the certificate config file.
68
+
69
+ The path to the certificate config file is read from the
70
+ GOOGLE_API_CERTIFICATE_CONFIG environment variable. This function
71
+ implements a retry mechanism to handle cases where the environment
72
+ variable is set before the files are available on the filesystem.
73
+
74
+ Returns:
75
+ str: The path to the leaf certificate file.
76
+
77
+ Raises:
78
+ google.auth.exceptions.RefreshError: If the certificate config file
79
+ or the certificate file cannot be found after retries.
80
+ """
81
+ import json
82
+
83
+ cert_config_path = os.environ.get(environment_vars.GOOGLE_API_CERTIFICATE_CONFIG)
84
+ if not cert_config_path:
85
+ return None
86
+
87
+ has_logged_warning = False
88
+
89
+ for interval in _POLLING_INTERVALS:
90
+ try:
91
+ with open(cert_config_path, "r") as f:
92
+ cert_config = json.load(f)
93
+ cert_path = (
94
+ cert_config.get("cert_configs", {})
95
+ .get("workload", {})
96
+ .get("cert_path")
97
+ )
98
+ if _is_certificate_file_ready(cert_path):
99
+ return cert_path
100
+ except (IOError, ValueError, KeyError):
101
+ if not has_logged_warning:
102
+ _LOGGER.warning(
103
+ "Certificate config file not found at %s (from %s environment "
104
+ "variable). Retrying for up to %s seconds.",
105
+ cert_config_path,
106
+ environment_vars.GOOGLE_API_CERTIFICATE_CONFIG,
107
+ _TOTAL_TIMEOUT,
108
+ )
109
+ has_logged_warning = True
110
+ pass
111
+
112
+ # As a fallback, check the well-known certificate path.
113
+ if _is_certificate_file_ready(_WELL_KNOWN_CERT_PATH):
114
+ return _WELL_KNOWN_CERT_PATH
115
+
116
+ # A sleep is required in two cases:
117
+ # 1. The config file is not found (the except block).
118
+ # 2. The config file is found, but the certificate is not yet available.
119
+ # In both cases, we need to poll, so we sleep on every iteration
120
+ # that doesn't return a certificate.
121
+ time.sleep(interval)
122
+
123
+ raise exceptions.RefreshError(
124
+ "Certificate config or certificate file not found after multiple retries. "
125
+ f"Token binding protection is failing. You can turn off this protection by setting "
126
+ f"{environment_vars.GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES} to false "
127
+ "to fall back to unbound tokens."
128
+ )
129
+
130
+
131
+ def get_and_parse_agent_identity_certificate():
132
+ """Gets and parses the agent identity certificate if not opted out.
133
+
134
+ Checks if the user has opted out of certificate-bound tokens. If not,
135
+ it gets the certificate path, reads the file, and parses it.
136
+
137
+ Returns:
138
+ The parsed certificate object if found and not opted out, otherwise None.
139
+ """
140
+ # If the user has opted out of cert bound tokens, there is no need to
141
+ # look up the certificate.
142
+ is_opted_out = (
143
+ os.environ.get(
144
+ environment_vars.GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES,
145
+ "true",
146
+ ).lower()
147
+ == "false"
148
+ )
149
+ if is_opted_out:
150
+ return None
151
+
152
+ cert_path = get_agent_identity_certificate_path()
153
+ if not cert_path:
154
+ return None
155
+
156
+ with open(cert_path, "rb") as cert_file:
157
+ cert_bytes = cert_file.read()
158
+
159
+ return parse_certificate(cert_bytes)
160
+
161
+
162
+ def parse_certificate(cert_bytes):
163
+ """Parses a PEM-encoded certificate.
164
+
165
+ Args:
166
+ cert_bytes (bytes): The PEM-encoded certificate bytes.
167
+
168
+ Returns:
169
+ cryptography.x509.Certificate: The parsed certificate object.
170
+ """
171
+ try:
172
+ from cryptography import x509
173
+
174
+ return x509.load_pem_x509_certificate(cert_bytes)
175
+ except ImportError as e:
176
+ raise ImportError(CRYPTOGRAPHY_NOT_FOUND_ERROR) from e
177
+
178
+
179
+ def _is_agent_identity_certificate(cert):
180
+ """Checks if a certificate is an Agent Identity certificate.
181
+
182
+ This is determined by checking the Subject Alternative Name (SAN) for a
183
+ SPIFFE ID with a trust domain matching Agent Identity patterns.
184
+
185
+ Args:
186
+ cert (cryptography.x509.Certificate): The parsed certificate object.
187
+
188
+ Returns:
189
+ bool: True if the certificate is an Agent Identity certificate,
190
+ False otherwise.
191
+ """
192
+ try:
193
+ from cryptography import x509
194
+ from cryptography.x509.oid import ExtensionOID
195
+
196
+ try:
197
+ ext = cert.extensions.get_extension_for_oid(
198
+ ExtensionOID.SUBJECT_ALTERNATIVE_NAME
199
+ )
200
+ except x509.ExtensionNotFound:
201
+ return False
202
+ uris = ext.value.get_values_for_type(x509.UniformResourceIdentifier)
203
+
204
+ for uri in uris:
205
+ parsed_uri = urlparse(uri)
206
+ if parsed_uri.scheme == "spiffe":
207
+ trust_domain = parsed_uri.netloc
208
+ for pattern in _AGENT_IDENTITY_SPIFFE_TRUST_DOMAIN_PATTERNS:
209
+ if re.match(pattern, trust_domain):
210
+ return True
211
+ return False
212
+ except ImportError as e:
213
+ raise ImportError(CRYPTOGRAPHY_NOT_FOUND_ERROR) from e
214
+
215
+
216
+ def calculate_certificate_fingerprint(cert):
217
+ """Calculates the URL-encoded, unpadded, base64-encoded SHA256 hash of a
218
+ DER-encoded certificate.
219
+
220
+ Args:
221
+ cert (cryptography.x509.Certificate): The parsed certificate object.
222
+
223
+ Returns:
224
+ str: The URL-encoded, unpadded, base64-encoded SHA256 fingerprint.
225
+ """
226
+ try:
227
+ from cryptography.hazmat.primitives import serialization
228
+
229
+ der_cert = cert.public_bytes(serialization.Encoding.DER)
230
+ fingerprint = hashlib.sha256(der_cert).digest()
231
+ # The certificate fingerprint is generated in two steps to align with GFE's
232
+ # expectations and ensure proper URL transmission:
233
+ # 1. Standard base64 encoding is applied, and padding ('=') is removed.
234
+ # 2. The resulting string is then URL-encoded to handle special characters
235
+ # ('+', '/') that would otherwise be misinterpreted in URL parameters.
236
+ base64_fingerprint = base64.b64encode(fingerprint).decode("utf-8")
237
+ unpadded_base64_fingerprint = base64_fingerprint.rstrip("=")
238
+ return quote(unpadded_base64_fingerprint)
239
+ except ImportError as e:
240
+ raise ImportError(CRYPTOGRAPHY_NOT_FOUND_ERROR) from e
241
+
242
+
243
+ def should_request_bound_token(cert):
244
+ """Determines if a bound token should be requested.
245
+
246
+ This is based on the GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES
247
+ environment variable and whether the certificate is an agent identity cert.
248
+
249
+ Args:
250
+ cert (cryptography.x509.Certificate): The parsed certificate object.
251
+
252
+ Returns:
253
+ bool: True if a bound token should be requested, False otherwise.
254
+ """
255
+ is_agent_cert = _is_agent_identity_certificate(cert)
256
+ is_opted_in = (
257
+ os.environ.get(
258
+ environment_vars.GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES,
259
+ "true",
260
+ ).lower()
261
+ == "true"
262
+ )
263
+ return is_agent_cert and is_opted_in
264
+
265
+
266
+ def call_client_cert_callback():
267
+ """Calls the client cert callback and returns the certificate and key."""
268
+ _, cert_bytes, key_bytes, passphrase = _mtls_helper.get_client_ssl_credentials(
269
+ generate_encrypted_key=True
270
+ )
271
+ return cert_bytes, key_bytes
272
+
273
+
274
+ def get_cached_cert_fingerprint(cached_cert):
275
+ """Returns the fingerprint of the cached certificate."""
276
+ if cached_cert:
277
+ cert_obj = parse_certificate(cached_cert)
278
+ cached_cert_fingerprint = calculate_certificate_fingerprint(cert_obj)
279
+ else:
280
+ raise ValueError("mTLS connection is not configured.")
281
+ return cached_cert_fingerprint
@@ -0,0 +1,153 @@
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
+ """Helpers for reading the Google Cloud SDK's configuration."""
16
+
17
+ import os
18
+ import subprocess
19
+
20
+ from google.auth import _helpers
21
+ from google.auth import environment_vars
22
+ from google.auth import exceptions
23
+
24
+
25
+ # The ~/.config subdirectory containing gcloud credentials.
26
+ _CONFIG_DIRECTORY = "gcloud"
27
+ # Windows systems store config at %APPDATA%\gcloud
28
+ _WINDOWS_CONFIG_ROOT_ENV_VAR = "APPDATA"
29
+ # The name of the file in the Cloud SDK config that contains default
30
+ # credentials.
31
+ _CREDENTIALS_FILENAME = "application_default_credentials.json"
32
+ # The name of the Cloud SDK shell script
33
+ _CLOUD_SDK_POSIX_COMMAND = "gcloud"
34
+ _CLOUD_SDK_WINDOWS_COMMAND = "gcloud.cmd"
35
+ # The command to get the Cloud SDK configuration
36
+ _CLOUD_SDK_CONFIG_GET_PROJECT_COMMAND = ("config", "get", "project")
37
+ # The command to get google user access token
38
+ _CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND = ("auth", "print-access-token")
39
+ # Cloud SDK's application-default client ID
40
+ CLOUD_SDK_CLIENT_ID = (
41
+ "764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com"
42
+ )
43
+
44
+
45
+ def get_config_path():
46
+ """Returns the absolute path the the Cloud SDK's configuration directory.
47
+
48
+ Returns:
49
+ str: The Cloud SDK config path.
50
+ """
51
+ # If the path is explicitly set, return that.
52
+ try:
53
+ return os.environ[environment_vars.CLOUD_SDK_CONFIG_DIR]
54
+ except KeyError:
55
+ pass
56
+
57
+ # Non-windows systems store this at ~/.config/gcloud
58
+ if os.name != "nt":
59
+ return os.path.join(os.path.expanduser("~"), ".config", _CONFIG_DIRECTORY)
60
+ # Windows systems store config at %APPDATA%\gcloud
61
+ else:
62
+ try:
63
+ return os.path.join(
64
+ os.environ[_WINDOWS_CONFIG_ROOT_ENV_VAR], _CONFIG_DIRECTORY
65
+ )
66
+ except KeyError:
67
+ # This should never happen unless someone is really
68
+ # messing with things, but we'll cover the case anyway.
69
+ drive = os.environ.get("SystemDrive", "C:")
70
+ return os.path.join(drive, "\\", _CONFIG_DIRECTORY)
71
+
72
+
73
+ def get_application_default_credentials_path():
74
+ """Gets the path to the application default credentials file.
75
+
76
+ The path may or may not exist.
77
+
78
+ Returns:
79
+ str: The full path to application default credentials.
80
+ """
81
+ config_path = get_config_path()
82
+ return os.path.join(config_path, _CREDENTIALS_FILENAME)
83
+
84
+
85
+ def _run_subprocess_ignore_stderr(command):
86
+ """Return subprocess.check_output with the given command and ignores stderr."""
87
+ with open(os.devnull, "w") as devnull:
88
+ output = subprocess.check_output(command, stderr=devnull)
89
+ return output
90
+
91
+
92
+ def get_project_id():
93
+ """Gets the project ID from the Cloud SDK.
94
+
95
+ Returns:
96
+ Optional[str]: The project ID.
97
+ """
98
+ if os.name == "nt":
99
+ command = _CLOUD_SDK_WINDOWS_COMMAND
100
+ else:
101
+ command = _CLOUD_SDK_POSIX_COMMAND
102
+
103
+ try:
104
+ # Ignore the stderr coming from gcloud, so it won't be mixed into the output.
105
+ # https://github.com/googleapis/google-auth-library-python/issues/673
106
+ project = _run_subprocess_ignore_stderr(
107
+ (command,) + _CLOUD_SDK_CONFIG_GET_PROJECT_COMMAND
108
+ )
109
+
110
+ # Turn bytes into a string and remove "\n"
111
+ project = _helpers.from_bytes(project).strip()
112
+ return project if project else None
113
+ except (subprocess.CalledProcessError, OSError, IOError):
114
+ return None
115
+
116
+
117
+ def get_auth_access_token(account=None):
118
+ """Load user access token with the ``gcloud auth print-access-token`` command.
119
+
120
+ Args:
121
+ account (Optional[str]): Account to get the access token for. If not
122
+ specified, the current active account will be used.
123
+
124
+ Returns:
125
+ str: The user access token.
126
+
127
+ Raises:
128
+ google.auth.exceptions.UserAccessTokenError: if failed to get access
129
+ token from gcloud.
130
+ """
131
+ if os.name == "nt":
132
+ command = _CLOUD_SDK_WINDOWS_COMMAND
133
+ else:
134
+ command = _CLOUD_SDK_POSIX_COMMAND
135
+
136
+ try:
137
+ if account:
138
+ command = (
139
+ (command,)
140
+ + _CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND
141
+ + ("--account=" + account,)
142
+ )
143
+ else:
144
+ command = (command,) + _CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND
145
+
146
+ access_token = subprocess.check_output(command, stderr=subprocess.STDOUT)
147
+ # remove the trailing "\n"
148
+ return access_token.decode("utf-8").strip()
149
+ except (subprocess.CalledProcessError, OSError, IOError) as caught_exc:
150
+ new_exc = exceptions.UserAccessTokenError(
151
+ "Failed to obtain access token", caught_exc
152
+ )
153
+ raise new_exc from caught_exc
@@ -0,0 +1,5 @@
1
+ """Shared constants."""
2
+
3
+ _SERVICE_ACCOUNT_TRUST_BOUNDARY_LOOKUP_ENDPOINT = "https://iamcredentials.{universe_domain}/v1/projects/-/serviceAccounts/{service_account_email}/allowedLocations"
4
+ _WORKFORCE_POOL_TRUST_BOUNDARY_LOOKUP_ENDPOINT = "https://iamcredentials.{universe_domain}/v1/locations/global/workforcePools/{pool_id}/allowedLocations"
5
+ _WORKLOAD_IDENTITY_POOL_TRUST_BOUNDARY_LOOKUP_ENDPOINT = "https://iamcredentials.{universe_domain}/v1/projects/{project_number}/locations/global/workloadIdentityPools/{pool_id}/allowedLocations"
@@ -0,0 +1,171 @@
1
+ # Copyright 2020 Google LLC
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
+
16
+ """Interfaces for credentials."""
17
+
18
+ import abc
19
+ import inspect
20
+
21
+ from google.auth import credentials
22
+
23
+
24
+ class Credentials(credentials.Credentials, metaclass=abc.ABCMeta):
25
+ """Async inherited credentials class from google.auth.credentials.
26
+ The added functionality is the before_request call which requires
27
+ async/await syntax.
28
+ All credentials have a :attr:`token` that is used for authentication and
29
+ may also optionally set an :attr:`expiry` to indicate when the token will
30
+ no longer be valid.
31
+
32
+ Most credentials will be :attr:`invalid` until :meth:`refresh` is called.
33
+ Credentials can do this automatically before the first HTTP request in
34
+ :meth:`before_request`.
35
+
36
+ Although the token and expiration will change as the credentials are
37
+ :meth:`refreshed <refresh>` and used, credentials should be considered
38
+ immutable. Various credentials will accept configuration such as private
39
+ keys, scopes, and other options. These options are not changeable after
40
+ construction. Some classes will provide mechanisms to copy the credentials
41
+ with modifications such as :meth:`ScopedCredentials.with_scopes`.
42
+ """
43
+
44
+ async def before_request(self, request, method, url, headers):
45
+ """Performs credential-specific before request logic.
46
+
47
+ Refreshes the credentials if necessary, then calls :meth:`apply` to
48
+ apply the token to the authentication header.
49
+
50
+ Args:
51
+ request (google.auth.transport.Request): The object used to make
52
+ HTTP requests.
53
+ method (str): The request's HTTP method or the RPC method being
54
+ invoked.
55
+ url (str): The request's URI or the RPC service's URI.
56
+ headers (Mapping): The request's headers.
57
+ """
58
+ # pylint: disable=unused-argument
59
+ # (Subclasses may use these arguments to ascertain information about
60
+ # the http request.)
61
+
62
+ if not self.valid:
63
+ if inspect.iscoroutinefunction(self.refresh):
64
+ await self.refresh(request)
65
+ else:
66
+ self.refresh(request)
67
+ self.apply(headers)
68
+
69
+
70
+ class CredentialsWithQuotaProject(credentials.CredentialsWithQuotaProject):
71
+ """Abstract base for credentials supporting ``with_quota_project`` factory"""
72
+
73
+
74
+ class AnonymousCredentials(credentials.AnonymousCredentials, Credentials):
75
+ """Credentials that do not provide any authentication information.
76
+
77
+ These are useful in the case of services that support anonymous access or
78
+ local service emulators that do not use credentials. This class inherits
79
+ from the sync anonymous credentials file, but is kept if async credentials
80
+ is initialized and we would like anonymous credentials.
81
+ """
82
+
83
+
84
+ class ReadOnlyScoped(credentials.ReadOnlyScoped, metaclass=abc.ABCMeta):
85
+ """Interface for credentials whose scopes can be queried.
86
+
87
+ OAuth 2.0-based credentials allow limiting access using scopes as described
88
+ in `RFC6749 Section 3.3`_.
89
+ If a credential class implements this interface then the credentials either
90
+ use scopes in their implementation.
91
+
92
+ Some credentials require scopes in order to obtain a token. You can check
93
+ if scoping is necessary with :attr:`requires_scopes`::
94
+
95
+ if credentials.requires_scopes:
96
+ # Scoping is required.
97
+ credentials = _credentials_async.with_scopes(scopes=['one', 'two'])
98
+
99
+ Credentials that require scopes must either be constructed with scopes::
100
+
101
+ credentials = SomeScopedCredentials(scopes=['one', 'two'])
102
+
103
+ Or must copy an existing instance using :meth:`with_scopes`::
104
+
105
+ scoped_credentials = _credentials_async.with_scopes(scopes=['one', 'two'])
106
+
107
+ Some credentials have scopes but do not allow or require scopes to be set,
108
+ these credentials can be used as-is.
109
+
110
+ .. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3
111
+ """
112
+
113
+
114
+ class Scoped(credentials.Scoped):
115
+ """Interface for credentials whose scopes can be replaced while copying.
116
+
117
+ OAuth 2.0-based credentials allow limiting access using scopes as described
118
+ in `RFC6749 Section 3.3`_.
119
+ If a credential class implements this interface then the credentials either
120
+ use scopes in their implementation.
121
+
122
+ Some credentials require scopes in order to obtain a token. You can check
123
+ if scoping is necessary with :attr:`requires_scopes`::
124
+
125
+ if credentials.requires_scopes:
126
+ # Scoping is required.
127
+ credentials = _credentials_async.create_scoped(['one', 'two'])
128
+
129
+ Credentials that require scopes must either be constructed with scopes::
130
+
131
+ credentials = SomeScopedCredentials(scopes=['one', 'two'])
132
+
133
+ Or must copy an existing instance using :meth:`with_scopes`::
134
+
135
+ scoped_credentials = credentials.with_scopes(scopes=['one', 'two'])
136
+
137
+ Some credentials have scopes but do not allow or require scopes to be set,
138
+ these credentials can be used as-is.
139
+
140
+ .. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3
141
+ """
142
+
143
+
144
+ def with_scopes_if_required(credentials, scopes):
145
+ """Creates a copy of the credentials with scopes if scoping is required.
146
+
147
+ This helper function is useful when you do not know (or care to know) the
148
+ specific type of credentials you are using (such as when you use
149
+ :func:`google.auth.default`). This function will call
150
+ :meth:`Scoped.with_scopes` if the credentials are scoped credentials and if
151
+ the credentials require scoping. Otherwise, it will return the credentials
152
+ as-is.
153
+
154
+ Args:
155
+ credentials (google.auth.credentials.Credentials): The credentials to
156
+ scope if necessary.
157
+ scopes (Sequence[str]): The list of scopes to use.
158
+
159
+ Returns:
160
+ google.auth._credentials_async.Credentials: Either a new set of scoped
161
+ credentials, or the passed in credentials instance if no scoping
162
+ was required.
163
+ """
164
+ if isinstance(credentials, Scoped) and credentials.requires_scopes:
165
+ return credentials.with_scopes(scopes)
166
+ else:
167
+ return credentials
168
+
169
+
170
+ class Signing(credentials.Signing, metaclass=abc.ABCMeta):
171
+ """Interface for credentials that can cryptographically sign messages."""