google-auth 2.40.2__tar.gz → 2.41.0__tar.gz

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 (179) hide show
  1. {google_auth-2.40.2/google_auth.egg-info → google_auth-2.41.0}/PKG-INFO +15 -3
  2. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/_default.py +49 -0
  3. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/_helpers.py +41 -0
  4. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/compute_engine/credentials.py +69 -7
  5. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/credentials.py +161 -18
  6. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/environment_vars.py +4 -0
  7. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/external_account.py +33 -10
  8. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/external_account_authorized_user.py +24 -1
  9. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/identity_pool.py +25 -1
  10. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/impersonated_credentials.py +57 -9
  11. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/pluggable.py +25 -1
  12. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/transport/__init__.py +1 -0
  13. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/version.py +1 -1
  14. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/_client.py +117 -0
  15. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/service_account.py +39 -4
  16. {google_auth-2.40.2 → google_auth-2.41.0/google_auth.egg-info}/PKG-INFO +15 -3
  17. {google_auth-2.40.2 → google_auth-2.41.0}/google_auth.egg-info/requires.txt +1 -1
  18. {google_auth-2.40.2 → google_auth-2.41.0}/google_auth.egg-info/top_level.txt +1 -0
  19. {google_auth-2.40.2 → google_auth-2.41.0}/setup.py +1 -1
  20. {google_auth-2.40.2 → google_auth-2.41.0}/tests/compute_engine/test_credentials.py +383 -5
  21. {google_auth-2.40.2 → google_auth-2.41.0}/tests/oauth2/test__client.py +176 -1
  22. {google_auth-2.40.2 → google_auth-2.41.0}/tests/oauth2/test_service_account.py +284 -9
  23. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test__helpers.py +28 -1
  24. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_aws.py +6 -4
  25. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_credentials.py +159 -27
  26. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_external_account.py +27 -25
  27. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_identity_pool.py +4 -2
  28. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_impersonated_credentials.py +296 -6
  29. {google_auth-2.40.2 → google_auth-2.41.0}/LICENSE +0 -0
  30. {google_auth-2.40.2 → google_auth-2.41.0}/MANIFEST.in +0 -0
  31. {google_auth-2.40.2 → google_auth-2.41.0}/README.rst +0 -0
  32. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/__init__.py +0 -0
  33. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/_cloud_sdk.py +0 -0
  34. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/_credentials_async.py +0 -0
  35. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/_credentials_base.py +0 -0
  36. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/_default_async.py +0 -0
  37. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/_exponential_backoff.py +0 -0
  38. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/_jwt_async.py +0 -0
  39. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/_oauth2client.py +0 -0
  40. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/_refresh_worker.py +0 -0
  41. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/_service_account_info.py +0 -0
  42. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/aio/__init__.py +0 -0
  43. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/aio/_helpers.py +0 -0
  44. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/aio/credentials.py +0 -0
  45. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/aio/transport/__init__.py +0 -0
  46. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/aio/transport/aiohttp.py +0 -0
  47. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/aio/transport/sessions.py +0 -0
  48. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/api_key.py +0 -0
  49. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/app_engine.py +0 -0
  50. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/aws.py +0 -0
  51. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/compute_engine/__init__.py +0 -0
  52. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/compute_engine/_metadata.py +0 -0
  53. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/crypt/__init__.py +0 -0
  54. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/crypt/_cryptography_rsa.py +0 -0
  55. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/crypt/_helpers.py +0 -0
  56. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/crypt/_python_rsa.py +0 -0
  57. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/crypt/base.py +0 -0
  58. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/crypt/es256.py +0 -0
  59. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/crypt/rsa.py +0 -0
  60. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/downscoped.py +0 -0
  61. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/exceptions.py +0 -0
  62. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/iam.py +0 -0
  63. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/jwt.py +0 -0
  64. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/metrics.py +0 -0
  65. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/py.typed +0 -0
  66. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/transport/_aiohttp_requests.py +0 -0
  67. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/transport/_custom_tls_signer.py +0 -0
  68. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/transport/_http_client.py +0 -0
  69. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/transport/_mtls_helper.py +0 -0
  70. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/transport/_requests_base.py +0 -0
  71. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/transport/grpc.py +0 -0
  72. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/transport/mtls.py +0 -0
  73. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/transport/requests.py +0 -0
  74. {google_auth-2.40.2 → google_auth-2.41.0}/google/auth/transport/urllib3.py +0 -0
  75. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/__init__.py +0 -0
  76. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/_client_async.py +0 -0
  77. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/_credentials_async.py +0 -0
  78. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/_id_token_async.py +0 -0
  79. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/_reauth_async.py +0 -0
  80. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/_service_account_async.py +0 -0
  81. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/challenges.py +0 -0
  82. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/credentials.py +0 -0
  83. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/gdch_credentials.py +0 -0
  84. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/id_token.py +0 -0
  85. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/py.typed +0 -0
  86. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/reauth.py +0 -0
  87. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/sts.py +0 -0
  88. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/utils.py +0 -0
  89. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/webauthn_handler.py +0 -0
  90. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/webauthn_handler_factory.py +0 -0
  91. {google_auth-2.40.2 → google_auth-2.41.0}/google/oauth2/webauthn_types.py +0 -0
  92. {google_auth-2.40.2 → google_auth-2.41.0}/google_auth.egg-info/SOURCES.txt +0 -0
  93. {google_auth-2.40.2 → google_auth-2.41.0}/google_auth.egg-info/dependency_links.txt +0 -0
  94. {google_auth-2.40.2 → google_auth-2.41.0}/setup.cfg +0 -0
  95. {google_auth-2.40.2 → google_auth-2.41.0}/tests/__init__.py +0 -0
  96. {google_auth-2.40.2 → google_auth-2.41.0}/tests/aio/test__helpers.py +0 -0
  97. {google_auth-2.40.2 → google_auth-2.41.0}/tests/compute_engine/__init__.py +0 -0
  98. {google_auth-2.40.2 → google_auth-2.41.0}/tests/compute_engine/data/smbios_product_name +0 -0
  99. {google_auth-2.40.2 → google_auth-2.41.0}/tests/compute_engine/data/smbios_product_name_non_google +0 -0
  100. {google_auth-2.40.2 → google_auth-2.41.0}/tests/compute_engine/test__metadata.py +0 -0
  101. {google_auth-2.40.2 → google_auth-2.41.0}/tests/conftest.py +0 -0
  102. {google_auth-2.40.2 → google_auth-2.41.0}/tests/crypt/__init__.py +0 -0
  103. {google_auth-2.40.2 → google_auth-2.41.0}/tests/crypt/test__cryptography_rsa.py +0 -0
  104. {google_auth-2.40.2 → google_auth-2.41.0}/tests/crypt/test__python_rsa.py +0 -0
  105. {google_auth-2.40.2 → google_auth-2.41.0}/tests/crypt/test_crypt.py +0 -0
  106. {google_auth-2.40.2 → google_auth-2.41.0}/tests/crypt/test_es256.py +0 -0
  107. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/authorized_user.json +0 -0
  108. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/authorized_user_cloud_sdk.json +0 -0
  109. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/authorized_user_cloud_sdk_with_quota_project_id.json +0 -0
  110. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/authorized_user_with_rapt_token.json +0 -0
  111. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/client_secrets.json +0 -0
  112. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/context_aware_metadata.json +0 -0
  113. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/enterprise_cert_invalid.json +0 -0
  114. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/enterprise_cert_valid.json +0 -0
  115. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/enterprise_cert_valid_provider.json +0 -0
  116. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/es256_privatekey.pem +0 -0
  117. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/es256_public_cert.pem +0 -0
  118. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/es256_publickey.pem +0 -0
  119. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/es256_service_account.json +0 -0
  120. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/external_account_authorized_user.json +0 -0
  121. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/external_account_authorized_user_non_gdu.json +0 -0
  122. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/external_subject_token.json +0 -0
  123. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/external_subject_token.txt +0 -0
  124. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/gdch_service_account.json +0 -0
  125. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/impersonated_service_account_authorized_user_source.json +0 -0
  126. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/impersonated_service_account_external_account_authorized_user_source.json +0 -0
  127. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/impersonated_service_account_service_account_source.json +0 -0
  128. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/impersonated_service_account_with_quota_project.json +0 -0
  129. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/old_oauth_credentials_py3.pickle +0 -0
  130. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/other_cert.pem +0 -0
  131. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/pem_from_pkcs12.pem +0 -0
  132. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/privatekey.p12 +0 -0
  133. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/privatekey.pem +0 -0
  134. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/privatekey.pub +0 -0
  135. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/public_cert.pem +0 -0
  136. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/service_account.json +0 -0
  137. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/service_account_non_gdu.json +0 -0
  138. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/trust_chain_with_leaf.pem +0 -0
  139. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/trust_chain_without_leaf.pem +0 -0
  140. {google_auth-2.40.2 → google_auth-2.41.0}/tests/data/trust_chain_wrong_order.pem +0 -0
  141. {google_auth-2.40.2 → google_auth-2.41.0}/tests/oauth2/__init__.py +0 -0
  142. {google_auth-2.40.2 → google_auth-2.41.0}/tests/oauth2/test_challenges.py +0 -0
  143. {google_auth-2.40.2 → google_auth-2.41.0}/tests/oauth2/test_credentials.py +0 -0
  144. {google_auth-2.40.2 → google_auth-2.41.0}/tests/oauth2/test_gdch_credentials.py +0 -0
  145. {google_auth-2.40.2 → google_auth-2.41.0}/tests/oauth2/test_id_token.py +0 -0
  146. {google_auth-2.40.2 → google_auth-2.41.0}/tests/oauth2/test_reauth.py +0 -0
  147. {google_auth-2.40.2 → google_auth-2.41.0}/tests/oauth2/test_sts.py +0 -0
  148. {google_auth-2.40.2 → google_auth-2.41.0}/tests/oauth2/test_utils.py +0 -0
  149. {google_auth-2.40.2 → google_auth-2.41.0}/tests/oauth2/test_webauthn_handler.py +0 -0
  150. {google_auth-2.40.2 → google_auth-2.41.0}/tests/oauth2/test_webauthn_handler_factory.py +0 -0
  151. {google_auth-2.40.2 → google_auth-2.41.0}/tests/oauth2/test_webauthn_types.py +0 -0
  152. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test__cloud_sdk.py +0 -0
  153. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test__default.py +0 -0
  154. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test__exponential_backoff.py +0 -0
  155. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test__oauth2client.py +0 -0
  156. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test__refresh_worker.py +0 -0
  157. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test__service_account_info.py +0 -0
  158. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_api_key.py +0 -0
  159. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_app_engine.py +0 -0
  160. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_credentials_async.py +0 -0
  161. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_downscoped.py +0 -0
  162. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_exceptions.py +0 -0
  163. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_external_account_authorized_user.py +0 -0
  164. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_iam.py +0 -0
  165. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_jwt.py +0 -0
  166. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_metrics.py +0 -0
  167. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_packaging.py +0 -0
  168. {google_auth-2.40.2 → google_auth-2.41.0}/tests/test_pluggable.py +0 -0
  169. {google_auth-2.40.2 → google_auth-2.41.0}/tests/transport/__init__.py +0 -0
  170. {google_auth-2.40.2 → google_auth-2.41.0}/tests/transport/aio/test_aiohttp.py +0 -0
  171. {google_auth-2.40.2 → google_auth-2.41.0}/tests/transport/aio/test_sessions.py +0 -0
  172. {google_auth-2.40.2 → google_auth-2.41.0}/tests/transport/compliance.py +0 -0
  173. {google_auth-2.40.2 → google_auth-2.41.0}/tests/transport/test__custom_tls_signer.py +0 -0
  174. {google_auth-2.40.2 → google_auth-2.41.0}/tests/transport/test__http_client.py +0 -0
  175. {google_auth-2.40.2 → google_auth-2.41.0}/tests/transport/test__mtls_helper.py +0 -0
  176. {google_auth-2.40.2 → google_auth-2.41.0}/tests/transport/test_grpc.py +0 -0
  177. {google_auth-2.40.2 → google_auth-2.41.0}/tests/transport/test_mtls.py +0 -0
  178. {google_auth-2.40.2 → google_auth-2.41.0}/tests/transport/test_requests.py +0 -0
  179. {google_auth-2.40.2 → google_auth-2.41.0}/tests/transport/test_urllib3.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: google-auth
3
- Version: 2.40.2
3
+ Version: 2.41.0
4
4
  Summary: Google Authentication Library
5
5
  Home-page: https://github.com/googleapis/google-auth-library-python
6
6
  Author: Google Cloud Platform
@@ -25,7 +25,7 @@ Classifier: Operating System :: OS Independent
25
25
  Classifier: Topic :: Internet :: WWW/HTTP
26
26
  Requires-Python: >=3.7
27
27
  License-File: LICENSE
28
- Requires-Dist: cachetools<6.0,>=2.0.0
28
+ Requires-Dist: cachetools<7.0,>=2.0.0
29
29
  Requires-Dist: pyasn1-modules>=0.2.1
30
30
  Requires-Dist: rsa<5,>=3.1.4
31
31
  Provides-Extra: aiohttp
@@ -74,6 +74,18 @@ Requires-Dist: aiohttp<3.10.0; extra == "testing"
74
74
  Provides-Extra: urllib3
75
75
  Requires-Dist: urllib3; extra == "urllib3"
76
76
  Requires-Dist: packaging; extra == "urllib3"
77
+ Dynamic: author
78
+ Dynamic: author-email
79
+ Dynamic: classifier
80
+ Dynamic: description
81
+ Dynamic: home-page
82
+ Dynamic: keywords
83
+ Dynamic: license
84
+ Dynamic: license-file
85
+ Dynamic: provides-extra
86
+ Dynamic: requires-dist
87
+ Dynamic: requires-python
88
+ Dynamic: summary
77
89
 
78
90
  Google Auth Python Library
79
91
  ==========================
@@ -59,6 +59,38 @@ or "API not enabled" error. See the following page for troubleshooting: \
59
59
  https://cloud.google.com/docs/authentication/adc-troubleshooting/user-creds. \
60
60
  """
61
61
 
62
+ _GENERIC_LOAD_METHOD_WARNING = """\
63
+ The {} method is deprecated because of a potential security risk.
64
+
65
+ This method does not validate the credential configuration. The security
66
+ risk occurs when a credential configuration is accepted from a source that
67
+ is not under your control and used without validation on your side.
68
+
69
+ If you know that you will be loading credential configurations of a
70
+ specific type, it is recommended to use a credential-type-specific
71
+ load method.
72
+ This will ensure that an unexpected credential type with potential for
73
+ malicious intent is not loaded unintentionally. You might still have to do
74
+ validation for certain credential types. Please follow the recommendations
75
+ for that method. For example, if you want to load only service accounts,
76
+ you can create the service account credentials explicitly:
77
+
78
+ ```
79
+ from google.oauth2 import service_account
80
+ creds = service_account.Credentials.from_service_account_file(filename)
81
+ ```
82
+
83
+ If you are loading your credential configuration from an untrusted source and have
84
+ not mitigated the risks (e.g. by validating the configuration yourself), make
85
+ these changes as soon as possible to prevent security risks to your environment.
86
+
87
+ Regardless of the method used, it is always your responsibility to validate
88
+ configurations received from external sources.
89
+
90
+ Refer to https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
91
+ for more details.
92
+ """
93
+
62
94
  # The subject token type used for AWS external_account credentials.
63
95
  _AWS_SUBJECT_TOKEN_TYPE = "urn:ietf:params:aws:token-type:aws4_request"
64
96
 
@@ -76,6 +108,20 @@ def _warn_about_problematic_credentials(credentials):
76
108
  warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING)
77
109
 
78
110
 
111
+ def _warn_about_generic_load_method(method_name): # pragma: NO COVER
112
+ """Warns that a generic load method is being used.
113
+
114
+ This is to discourage use of the generic load methods in favor of
115
+ more specific methods. The generic methods are more likely to lead to
116
+ security issues if the input is not validated.
117
+
118
+ Args:
119
+ method_name (str): The name of the method being used.
120
+ """
121
+
122
+ warnings.warn(_GENERIC_LOAD_METHOD_WARNING.format(method_name), DeprecationWarning)
123
+
124
+
79
125
  def load_credentials_from_file(
80
126
  filename, scopes=None, default_scopes=None, quota_project_id=None, request=None
81
127
  ):
@@ -121,6 +167,8 @@ def load_credentials_from_file(
121
167
  google.auth.exceptions.DefaultCredentialsError: if the file is in the
122
168
  wrong format or is missing.
123
169
  """
170
+ _warn_about_generic_load_method("load_credentials_from_file")
171
+
124
172
  if not os.path.exists(filename):
125
173
  raise exceptions.DefaultCredentialsError(
126
174
  "File {} was not found.".format(filename)
@@ -184,6 +232,7 @@ def load_credentials_from_dict(
184
232
  google.auth.exceptions.DefaultCredentialsError: if the file is in the
185
233
  wrong format or is missing.
186
234
  """
235
+ _warn_about_generic_load_method("load_credentials_from_dict")
187
236
  if not isinstance(info, dict):
188
237
  raise exceptions.DefaultCredentialsError(
189
238
  "info object was of type {} but dict type was expected.".format(type(info))
@@ -21,6 +21,7 @@ from email.message import Message
21
21
  import hashlib
22
22
  import json
23
23
  import logging
24
+ import os
24
25
  import sys
25
26
  from typing import Any, Dict, Mapping, Optional, Union
26
27
  import urllib
@@ -287,6 +288,46 @@ def unpadded_urlsafe_b64encode(value):
287
288
  return base64.urlsafe_b64encode(value).rstrip(b"=")
288
289
 
289
290
 
291
+ def get_bool_from_env(variable_name, default=False):
292
+ """Gets a boolean value from an environment variable.
293
+
294
+ The environment variable is interpreted as a boolean with the following
295
+ (case-insensitive) rules:
296
+ - "true", "1" are considered true.
297
+ - "false", "0" are considered false.
298
+ Any other values will raise an exception.
299
+
300
+ Args:
301
+ variable_name (str): The name of the environment variable.
302
+ default (bool): The default value if the environment variable is not
303
+ set.
304
+
305
+ Returns:
306
+ bool: The boolean value of the environment variable.
307
+
308
+ Raises:
309
+ google.auth.exceptions.InvalidValue: If the environment variable is
310
+ set to a value that can not be interpreted as a boolean.
311
+ """
312
+ value = os.environ.get(variable_name)
313
+
314
+ if value is None:
315
+ return default
316
+
317
+ value = value.lower()
318
+
319
+ if value in ("true", "1"):
320
+ return True
321
+ elif value in ("false", "0"):
322
+ return False
323
+ else:
324
+ raise exceptions.InvalidValue(
325
+ 'Environment variable "{}" must be one of "true", "false", "1", or "0".'.format(
326
+ variable_name
327
+ )
328
+ )
329
+
330
+
290
331
  def is_python_3():
291
332
  """Check if the Python interpreter is Python 2 or 3.
292
333
 
@@ -30,11 +30,16 @@ from google.auth import metrics
30
30
  from google.auth.compute_engine import _metadata
31
31
  from google.oauth2 import _client
32
32
 
33
+ _TRUST_BOUNDARY_LOOKUP_ENDPOINT = (
34
+ "https://iamcredentials.{}/v1/projects/-/serviceAccounts/{}/allowedLocations"
35
+ )
36
+
33
37
 
34
38
  class Credentials(
35
39
  credentials.Scoped,
36
40
  credentials.CredentialsWithQuotaProject,
37
41
  credentials.CredentialsWithUniverseDomain,
42
+ credentials.CredentialsWithTrustBoundary,
38
43
  ):
39
44
  """Compute Engine Credentials.
40
45
 
@@ -61,6 +66,7 @@ class Credentials(
61
66
  scopes=None,
62
67
  default_scopes=None,
63
68
  universe_domain=None,
69
+ trust_boundary=None,
64
70
  ):
65
71
  """
66
72
  Args:
@@ -76,6 +82,7 @@ class Credentials(
76
82
  provided or None, credential will attempt to fetch the value
77
83
  from metadata server. If metadata server doesn't have universe
78
84
  domain endpoint, then the default googleapis.com will be used.
85
+ trust_boundary (Mapping[str,str]): A credential trust boundary.
79
86
  """
80
87
  super(Credentials, self).__init__()
81
88
  self._service_account_email = service_account_email
@@ -86,6 +93,7 @@ class Credentials(
86
93
  if universe_domain:
87
94
  self._universe_domain = universe_domain
88
95
  self._universe_domain_cached = True
96
+ self._trust_boundary = trust_boundary
89
97
 
90
98
  def _retrieve_info(self, request):
91
99
  """Retrieve information about the service account.
@@ -100,16 +108,22 @@ class Credentials(
100
108
  request, service_account=self._service_account_email
101
109
  )
102
110
 
111
+ if not info or "email" not in info:
112
+ raise exceptions.RefreshError(
113
+ "Unexpected response from metadata server: "
114
+ "service account info is missing 'email' field."
115
+ )
116
+
103
117
  self._service_account_email = info["email"]
104
118
 
105
119
  # Don't override scopes requested by the user.
106
120
  if self._scopes is None:
107
- self._scopes = info["scopes"]
121
+ self._scopes = info.get("scopes")
108
122
 
109
123
  def _metric_header_for_usage(self):
110
124
  return metrics.CRED_TYPE_SA_MDS
111
125
 
112
- def refresh(self, request):
126
+ def _refresh_token(self, request):
113
127
  """Refresh the access token and scopes.
114
128
 
115
129
  Args:
@@ -124,13 +138,45 @@ class Credentials(
124
138
  scopes = self._scopes if self._scopes is not None else self._default_scopes
125
139
  try:
126
140
  self._retrieve_info(request)
141
+ # Always fetch token with default service account email.
127
142
  self.token, self.expiry = _metadata.get_service_account_token(
128
- request, service_account=self._service_account_email, scopes=scopes
143
+ request, service_account="default", scopes=scopes
129
144
  )
130
145
  except exceptions.TransportError as caught_exc:
131
146
  new_exc = exceptions.RefreshError(caught_exc)
132
147
  raise new_exc from caught_exc
133
148
 
149
+ def _build_trust_boundary_lookup_url(self):
150
+ """Builds and returns the URL for the trust boundary lookup API for GCE."""
151
+ # If the service account email is 'default', we need to get the
152
+ # actual email address from the metadata server.
153
+ if self._service_account_email == "default":
154
+ from google.auth.transport import requests as google_auth_requests
155
+
156
+ request = google_auth_requests.Request()
157
+ try:
158
+ info = _metadata.get_service_account_info(request, "default")
159
+ if not info or "email" not in info:
160
+ raise exceptions.RefreshError(
161
+ "Unexpected response from metadata server: "
162
+ "service account info is missing 'email' field."
163
+ )
164
+ self._service_account_email = info["email"]
165
+
166
+ except exceptions.TransportError as e:
167
+ # If fetching the service account email fails due to a transport error,
168
+ # it means we cannot build the trust boundary lookup URL.
169
+ # Wrap this in a RefreshError so it's caught by _refresh_trust_boundary.
170
+ raise exceptions.RefreshError(
171
+ "Failed to get service account email for trust boundary lookup: {}".format(
172
+ e
173
+ )
174
+ ) from e
175
+
176
+ return _TRUST_BOUNDARY_LOOKUP_ENDPOINT.format(
177
+ self.universe_domain, self.service_account_email
178
+ )
179
+
134
180
  @property
135
181
  def service_account_email(self):
136
182
  """The service account email.
@@ -172,8 +218,9 @@ class Credentials(
172
218
  quota_project_id=quota_project_id,
173
219
  scopes=self._scopes,
174
220
  default_scopes=self._default_scopes,
221
+ universe_domain=self._universe_domain,
222
+ trust_boundary=self._trust_boundary,
175
223
  )
176
- creds._universe_domain = self._universe_domain
177
224
  creds._universe_domain_cached = self._universe_domain_cached
178
225
  return creds
179
226
 
@@ -187,8 +234,9 @@ class Credentials(
187
234
  default_scopes=default_scopes,
188
235
  service_account_email=self._service_account_email,
189
236
  quota_project_id=self._quota_project_id,
237
+ universe_domain=self._universe_domain,
238
+ trust_boundary=self._trust_boundary,
190
239
  )
191
- creds._universe_domain = self._universe_domain
192
240
  creds._universe_domain_cached = self._universe_domain_cached
193
241
  return creds
194
242
 
@@ -199,9 +247,23 @@ class Credentials(
199
247
  default_scopes=self._default_scopes,
200
248
  service_account_email=self._service_account_email,
201
249
  quota_project_id=self._quota_project_id,
250
+ trust_boundary=self._trust_boundary,
202
251
  universe_domain=universe_domain,
203
252
  )
204
253
 
254
+ @_helpers.copy_docstring(credentials.CredentialsWithTrustBoundary)
255
+ def with_trust_boundary(self, trust_boundary):
256
+ creds = self.__class__(
257
+ service_account_email=self._service_account_email,
258
+ quota_project_id=self._quota_project_id,
259
+ scopes=self._scopes,
260
+ default_scopes=self._default_scopes,
261
+ universe_domain=self._universe_domain,
262
+ trust_boundary=trust_boundary,
263
+ )
264
+ creds._universe_domain_cached = self._universe_domain_cached
265
+ return creds
266
+
205
267
 
206
268
  _DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
207
269
  _DEFAULT_TOKEN_URI = "https://www.googleapis.com/oauth2/v4/token"
@@ -274,7 +336,7 @@ class IDTokenCredentials(
274
336
 
275
337
  if use_metadata_identity_endpoint:
276
338
  if token_uri or additional_claims or service_account_email or signer:
277
- raise exceptions.MalformedError(
339
+ raise ValueError(
278
340
  "If use_metadata_identity_endpoint is set, token_uri, "
279
341
  "additional_claims, service_account_email, signer arguments"
280
342
  " must not be set"
@@ -365,7 +427,7 @@ class IDTokenCredentials(
365
427
  # since the signer is already instantiated,
366
428
  # the request is not needed
367
429
  if self._use_metadata_identity_endpoint:
368
- raise exceptions.MalformedError(
430
+ raise ValueError(
369
431
  "If use_metadata_identity_endpoint is set, token_uri" " must not be set"
370
432
  )
371
433
  else:
@@ -18,14 +18,18 @@
18
18
  import abc
19
19
  from enum import Enum
20
20
  import os
21
+ from typing import List
21
22
 
22
23
  from google.auth import _helpers, environment_vars
23
24
  from google.auth import exceptions
24
25
  from google.auth import metrics
25
26
  from google.auth._credentials_base import _BaseCredentials
27
+ from google.auth._default import _LOGGER
26
28
  from google.auth._refresh_worker import RefreshThreadManager
27
29
 
28
30
  DEFAULT_UNIVERSE_DOMAIN = "googleapis.com"
31
+ NO_OP_TRUST_BOUNDARY_LOCATIONS: List[str] = []
32
+ NO_OP_TRUST_BOUNDARY_ENCODED_LOCATIONS = "0x0"
29
33
 
30
34
 
31
35
  class Credentials(_BaseCredentials):
@@ -178,22 +182,7 @@ class Credentials(_BaseCredentials):
178
182
  token (Optional[str]): If specified, overrides the current access
179
183
  token.
180
184
  """
181
- self._apply(headers, token=token)
182
- """Trust boundary value will be a cached value from global lookup.
183
-
184
- The response of trust boundary will be a list of regions and a hex
185
- encoded representation.
186
-
187
- An example of global lookup response:
188
- {
189
- "locations": [
190
- "us-central1", "us-east1", "europe-west1", "asia-east1"
191
- ]
192
- "encoded_locations": "0xA30"
193
- }
194
- """
195
- if self._trust_boundary is not None:
196
- headers["x-allowed-locations"] = self._trust_boundary["encoded_locations"]
185
+ self._apply(headers, token)
197
186
  if self.quota_project_id:
198
187
  headers["x-goog-user-project"] = self.quota_project_id
199
188
 
@@ -299,6 +288,161 @@ class CredentialsWithUniverseDomain(Credentials):
299
288
  )
300
289
 
301
290
 
291
+ class CredentialsWithTrustBoundary(Credentials):
292
+ """Abstract base for credentials supporting ``with_trust_boundary`` factory"""
293
+
294
+ @abc.abstractmethod
295
+ def _refresh_token(self, request):
296
+ """Refreshes the access token.
297
+
298
+ Args:
299
+ request (google.auth.transport.Request): The object used to make
300
+ HTTP requests.
301
+
302
+ Raises:
303
+ google.auth.exceptions.RefreshError: If the credentials could
304
+ not be refreshed.
305
+ """
306
+ raise NotImplementedError("_refresh_token must be implemented")
307
+
308
+ def with_trust_boundary(self, trust_boundary):
309
+ """Returns a copy of these credentials with a modified trust boundary.
310
+
311
+ Args:
312
+ trust_boundary Mapping[str, str]: The trust boundary to use for the
313
+ credential. This should be a map with a "locations" key that maps to
314
+ a list of GCP regions, and a "encodedLocations" key that maps to a
315
+ hex string.
316
+
317
+ Returns:
318
+ google.auth.credentials.Credentials: A new credentials instance.
319
+ """
320
+ raise NotImplementedError("This credential does not support trust boundaries.")
321
+
322
+ def _is_trust_boundary_lookup_required(self):
323
+ """Checks if a trust boundary lookup is required.
324
+
325
+ A lookup is required if the feature is enabled via an environment
326
+ variable, the universe domain is supported, and a no-op boundary
327
+ is not already cached.
328
+
329
+ Returns:
330
+ bool: True if a trust boundary lookup is required, False otherwise.
331
+ """
332
+ # 1. Check if the feature is enabled via environment variable.
333
+ if not _helpers.get_bool_from_env(
334
+ environment_vars.GOOGLE_AUTH_TRUST_BOUNDARY_ENABLED, default=False
335
+ ):
336
+ return False
337
+
338
+ # 2. Skip trust boundary flow for non-default universe domains.
339
+ if self.universe_domain != DEFAULT_UNIVERSE_DOMAIN:
340
+ return False
341
+
342
+ # 3. Do not trigger refresh if credential has a cached no-op trust boundary.
343
+ return not self._has_no_op_trust_boundary()
344
+
345
+ def _get_trust_boundary_header(self):
346
+ if self._trust_boundary is not None:
347
+ if self._has_no_op_trust_boundary():
348
+ # STS expects an empty string if the trust boundary value is no-op.
349
+ return {"x-allowed-locations": ""}
350
+ else:
351
+ return {"x-allowed-locations": self._trust_boundary["encodedLocations"]}
352
+ return {}
353
+
354
+ def apply(self, headers, token=None):
355
+ """Apply the token to the authentication header."""
356
+ super().apply(headers, token)
357
+ headers.update(self._get_trust_boundary_header())
358
+
359
+ def refresh(self, request):
360
+ """Refreshes the access token and the trust boundary.
361
+
362
+ This method calls the subclass's token refresh logic and then
363
+ refreshes the trust boundary if applicable.
364
+ """
365
+ self._refresh_token(request)
366
+ self._refresh_trust_boundary(request)
367
+
368
+ def _refresh_trust_boundary(self, request):
369
+ """Triggers a refresh of the trust boundary and updates the cache if necessary.
370
+
371
+ Args:
372
+ request (google.auth.transport.Request): The object used to make
373
+ HTTP requests.
374
+
375
+ Raises:
376
+ google.auth.exceptions.RefreshError: If the trust boundary could
377
+ not be refreshed and no cached value is available.
378
+ """
379
+ if not self._is_trust_boundary_lookup_required():
380
+ return
381
+ try:
382
+ self._trust_boundary = self._lookup_trust_boundary(request)
383
+ except exceptions.RefreshError as error:
384
+ # If the call to the lookup API failed, check if there is a trust boundary
385
+ # already cached. If there is, do nothing. If not, then throw the error.
386
+ if self._trust_boundary is None:
387
+ raise error
388
+ if _helpers.is_logging_enabled(_LOGGER):
389
+ _LOGGER.debug(
390
+ "Using cached trust boundary due to refresh error: %s", error
391
+ )
392
+ return
393
+
394
+ def _lookup_trust_boundary(self, request):
395
+ """Calls the trust boundary lookup API to refresh the trust boundary cache.
396
+
397
+ Args:
398
+ request (google.auth.transport.Request): The object used to make
399
+ HTTP requests.
400
+
401
+ Returns:
402
+ trust_boundary (dict): The trust boundary object returned by the lookup API.
403
+
404
+ Raises:
405
+ google.auth.exceptions.RefreshError: If the trust boundary could not be
406
+ retrieved.
407
+ """
408
+ from google.oauth2 import _client
409
+
410
+ url = self._build_trust_boundary_lookup_url()
411
+ if not url:
412
+ raise exceptions.InvalidValue("Failed to build trust boundary lookup URL.")
413
+
414
+ headers = {}
415
+ self._apply(headers)
416
+ headers.update(self._get_trust_boundary_header())
417
+ return _client._lookup_trust_boundary(request, url, headers=headers)
418
+
419
+ @abc.abstractmethod
420
+ def _build_trust_boundary_lookup_url(self):
421
+ """
422
+ Builds and returns the URL for the trust boundary lookup API.
423
+
424
+ This method should be implemented by subclasses to provide the
425
+ specific URL based on the credential type and its properties.
426
+
427
+ Returns:
428
+ str: The URL for the trust boundary lookup endpoint, or None
429
+ if lookup should be skipped (e.g., for non-applicable universe domains).
430
+ """
431
+ raise NotImplementedError(
432
+ "_build_trust_boundary_lookup_url must be implemented"
433
+ )
434
+
435
+ def _has_no_op_trust_boundary(self):
436
+ # A no-op trust boundary is indicated by encodedLocations being "0x0".
437
+ # The "locations" list may or may not be present as an empty list.
438
+ if self._trust_boundary is None:
439
+ return False
440
+ return (
441
+ self._trust_boundary.get("encodedLocations")
442
+ == NO_OP_TRUST_BOUNDARY_ENCODED_LOCATIONS
443
+ )
444
+
445
+
302
446
  class AnonymousCredentials(Credentials):
303
447
  """Credentials that do not provide any authentication information.
304
448
 
@@ -382,8 +526,7 @@ class ReadOnlyScoped(metaclass=abc.ABCMeta):
382
526
 
383
527
  @abc.abstractproperty
384
528
  def requires_scopes(self):
385
- """True if these credentials require scopes to obtain an access token.
386
- """
529
+ """True if these credentials require scopes to obtain an access token."""
387
530
  return False
388
531
 
389
532
  def has_scopes(self, scopes):
@@ -82,3 +82,7 @@ AWS_SECRET_ACCESS_KEY = "AWS_SECRET_ACCESS_KEY"
82
82
  AWS_SESSION_TOKEN = "AWS_SESSION_TOKEN"
83
83
  AWS_REGION = "AWS_REGION"
84
84
  AWS_DEFAULT_REGION = "AWS_DEFAULT_REGION"
85
+
86
+ GOOGLE_AUTH_TRUST_BOUNDARY_ENABLED = "GOOGLE_AUTH_TRUST_BOUNDARY_ENABLED"
87
+ """Environment variable controlling whether to enable trust boundary feature.
88
+ The default value is false. Users have to explicitly set this value to true."""
@@ -59,18 +59,18 @@ _DEFAULT_TOKEN_URL = "https://sts.{universe_domain}/v1/token"
59
59
  @dataclass
60
60
  class SupplierContext:
61
61
  """A context class that contains information about the requested third party credential that is passed
62
- to AWS security credential and subject token suppliers.
62
+ to AWS security credential and subject token suppliers.
63
63
 
64
- Attributes:
65
- subject_token_type (str): The requested subject token type based on the Oauth2.0 token exchange spec.
66
- Expected values include::
64
+ Attributes:
65
+ subject_token_type (str): The requested subject token type based on the Oauth2.0 token exchange spec.
66
+ Expected values include::
67
67
 
68
- “urn:ietf:params:oauth:token-type:jwt”
69
- “urn:ietf:params:oauth:token-type:id-token”
70
- “urn:ietf:params:oauth:token-type:saml2”
71
- “urn:ietf:params:aws:token-type:aws4_request”
68
+ “urn:ietf:params:oauth:token-type:jwt”
69
+ “urn:ietf:params:oauth:token-type:id-token”
70
+ “urn:ietf:params:oauth:token-type:saml2”
71
+ “urn:ietf:params:aws:token-type:aws4_request”
72
72
 
73
- audience (str): The requested audience for the subject token.
73
+ audience (str): The requested audience for the subject token.
74
74
  """
75
75
 
76
76
  subject_token_type: str
@@ -89,7 +89,14 @@ class Credentials(
89
89
  credentials for Google access token and authorizing requests to Google APIs.
90
90
  The base class implements the common logic for exchanging external account
91
91
  credentials for Google access tokens.
92
- """
92
+
93
+ **IMPORTANT**:
94
+ This class does not validate the credential configuration. A security
95
+ risk occurs when a credential configuration configured with malicious urls
96
+ is used.
97
+ When the credential configuration is accepted from an
98
+ untrusted source, you should validate it before using.
99
+ Refer https://cloud.google.com/docs/authentication/external/externally-sourced-credentials for more details."""
93
100
 
94
101
  def __init__(
95
102
  self,
@@ -576,6 +583,14 @@ class Credentials(
576
583
  def from_info(cls, info, **kwargs):
577
584
  """Creates a Credentials instance from parsed external account info.
578
585
 
586
+ **IMPORTANT**:
587
+ This method does not validate the credential configuration. A security
588
+ risk occurs when a credential configuration configured with malicious urls
589
+ is used.
590
+ When the credential configuration is accepted from an
591
+ untrusted source, you should validate it before using with this method.
592
+ Refer https://cloud.google.com/docs/authentication/external/externally-sourced-credentials for more details.
593
+
579
594
  Args:
580
595
  info (Mapping[str, str]): The external account info in Google
581
596
  format.
@@ -615,6 +630,14 @@ class Credentials(
615
630
  def from_file(cls, filename, **kwargs):
616
631
  """Creates a Credentials instance from an external account json file.
617
632
 
633
+ **IMPORTANT**:
634
+ This method does not validate the credential configuration. A security
635
+ risk occurs when a credential configuration configured with malicious urls
636
+ is used.
637
+ When the credential configuration is accepted from an
638
+ untrusted source, you should validate it before using with this method.
639
+ Refer https://cloud.google.com/docs/authentication/external/externally-sourced-credentials for more details.
640
+
618
641
  Args:
619
642
  filename (str): The path to the external account json file.
620
643
  kwargs: Additional arguments to pass to the constructor.
@@ -60,7 +60,14 @@ class Credentials(
60
60
  The credentials are considered immutable. If you want to modify the
61
61
  quota project, use `with_quota_project` and if you want to modify the token
62
62
  uri, use `with_token_uri`.
63
- """
63
+
64
+ **IMPORTANT**:
65
+ This class does not validate the credential configuration. A security
66
+ risk occurs when a credential configuration configured with malicious urls
67
+ is used.
68
+ When the credential configuration is accepted from an
69
+ untrusted source, you should validate it before using.
70
+ Refer https://cloud.google.com/docs/authentication/external/externally-sourced-credentials for more details."""
64
71
 
65
72
  def __init__(
66
73
  self,
@@ -328,6 +335,14 @@ class Credentials(
328
335
  def from_info(cls, info, **kwargs):
329
336
  """Creates a Credentials instance from parsed external account info.
330
337
 
338
+ **IMPORTANT**:
339
+ This method does not validate the credential configuration. A security
340
+ risk occurs when a credential configuration configured with malicious urls
341
+ is used.
342
+ When the credential configuration is accepted from an
343
+ untrusted source, you should validate it before using with this method.
344
+ Refer https://cloud.google.com/docs/authentication/external/externally-sourced-credentials for more details.
345
+
331
346
  Args:
332
347
  info (Mapping[str, str]): The external account info in Google
333
348
  format.
@@ -367,6 +382,14 @@ class Credentials(
367
382
  def from_file(cls, filename, **kwargs):
368
383
  """Creates a Credentials instance from an external account json file.
369
384
 
385
+ **IMPORTANT**:
386
+ This method does not validate the credential configuration. A security
387
+ risk occurs when a credential configuration configured with malicious urls
388
+ is used.
389
+ When the credential configuration is accepted from an
390
+ untrusted source, you should validate it before using with this method.
391
+ Refer https://cloud.google.com/docs/authentication/external/externally-sourced-credentials for more details.
392
+
370
393
  Args:
371
394
  filename (str): The path to the external account json file.
372
395
  kwargs: Additional arguments to pass to the constructor.