google-auth 2.44.0__tar.gz → 2.45.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 (190) hide show
  1. {google_auth-2.44.0/google_auth.egg-info → google_auth-2.45.0}/PKG-INFO +4 -1
  2. google_auth-2.45.0/google/auth/_agent_identity_utils.py +281 -0
  3. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_oauth2client.py +1 -1
  4. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/compute_engine/_metadata.py +10 -3
  5. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/compute_engine/credentials.py +1 -1
  6. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/environment_vars.py +9 -0
  7. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/external_account.py +11 -4
  8. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/identity_pool.py +22 -0
  9. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/_mtls_helper.py +29 -2
  10. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/requests.py +32 -1
  11. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/urllib3.py +40 -0
  12. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/version.py +1 -1
  13. {google_auth-2.44.0 → google_auth-2.45.0/google_auth.egg-info}/PKG-INFO +4 -1
  14. {google_auth-2.44.0 → google_auth-2.45.0}/google_auth.egg-info/SOURCES.txt +2 -0
  15. {google_auth-2.44.0 → google_auth-2.45.0}/google_auth.egg-info/requires.txt +6 -0
  16. {google_auth-2.44.0 → google_auth-2.45.0}/setup.py +1 -0
  17. {google_auth-2.44.0 → google_auth-2.45.0}/tests/compute_engine/test__metadata.py +100 -0
  18. {google_auth-2.44.0 → google_auth-2.45.0}/tests/compute_engine/test_credentials.py +72 -0
  19. google_auth-2.45.0/tests/test_agent_identity_utils.py +337 -0
  20. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_external_account.py +18 -0
  21. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_identity_pool.py +56 -0
  22. {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/test__mtls_helper.py +63 -1
  23. {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/test_requests.py +224 -0
  24. {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/test_urllib3.py +208 -0
  25. {google_auth-2.44.0 → google_auth-2.45.0}/LICENSE +0 -0
  26. {google_auth-2.44.0 → google_auth-2.45.0}/MANIFEST.in +0 -0
  27. {google_auth-2.44.0 → google_auth-2.45.0}/README.rst +0 -0
  28. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/__init__.py +0 -0
  29. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_cloud_sdk.py +0 -0
  30. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_constants.py +0 -0
  31. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_credentials_async.py +0 -0
  32. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_credentials_base.py +0 -0
  33. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_default.py +0 -0
  34. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_default_async.py +0 -0
  35. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_exponential_backoff.py +0 -0
  36. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_helpers.py +0 -0
  37. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_jwt_async.py +0 -0
  38. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_refresh_worker.py +0 -0
  39. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_service_account_info.py +0 -0
  40. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/aio/__init__.py +0 -0
  41. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/aio/_helpers.py +0 -0
  42. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/aio/credentials.py +0 -0
  43. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/aio/transport/__init__.py +0 -0
  44. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/aio/transport/aiohttp.py +0 -0
  45. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/aio/transport/sessions.py +0 -0
  46. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/api_key.py +0 -0
  47. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/app_engine.py +0 -0
  48. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/aws.py +0 -0
  49. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/compute_engine/__init__.py +0 -0
  50. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/compute_engine/_mtls.py +0 -0
  51. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/credentials.py +0 -0
  52. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/__init__.py +0 -0
  53. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/_cryptography_rsa.py +0 -0
  54. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/_helpers.py +0 -0
  55. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/_python_rsa.py +0 -0
  56. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/base.py +0 -0
  57. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/es.py +0 -0
  58. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/es256.py +0 -0
  59. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/rsa.py +0 -0
  60. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/downscoped.py +0 -0
  61. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/exceptions.py +0 -0
  62. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/external_account_authorized_user.py +0 -0
  63. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/iam.py +0 -0
  64. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/impersonated_credentials.py +0 -0
  65. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/jwt.py +0 -0
  66. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/metrics.py +0 -0
  67. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/pluggable.py +0 -0
  68. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/py.typed +0 -0
  69. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/__init__.py +0 -0
  70. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/_aiohttp_requests.py +0 -0
  71. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/_custom_tls_signer.py +0 -0
  72. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/_http_client.py +0 -0
  73. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/_requests_base.py +0 -0
  74. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/grpc.py +0 -0
  75. {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/mtls.py +0 -0
  76. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/__init__.py +0 -0
  77. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/_client.py +0 -0
  78. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/_client_async.py +0 -0
  79. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/_credentials_async.py +0 -0
  80. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/_id_token_async.py +0 -0
  81. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/_reauth_async.py +0 -0
  82. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/_service_account_async.py +0 -0
  83. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/challenges.py +0 -0
  84. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/credentials.py +0 -0
  85. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/gdch_credentials.py +0 -0
  86. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/id_token.py +0 -0
  87. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/py.typed +0 -0
  88. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/reauth.py +0 -0
  89. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/service_account.py +0 -0
  90. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/sts.py +0 -0
  91. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/utils.py +0 -0
  92. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/webauthn_handler.py +0 -0
  93. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/webauthn_handler_factory.py +0 -0
  94. {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/webauthn_types.py +0 -0
  95. {google_auth-2.44.0 → google_auth-2.45.0}/google_auth.egg-info/dependency_links.txt +0 -0
  96. {google_auth-2.44.0 → google_auth-2.45.0}/google_auth.egg-info/top_level.txt +0 -0
  97. {google_auth-2.44.0 → google_auth-2.45.0}/setup.cfg +0 -0
  98. {google_auth-2.44.0 → google_auth-2.45.0}/tests/__init__.py +0 -0
  99. {google_auth-2.44.0 → google_auth-2.45.0}/tests/aio/test__helpers.py +0 -0
  100. {google_auth-2.44.0 → google_auth-2.45.0}/tests/compute_engine/__init__.py +0 -0
  101. {google_auth-2.44.0 → google_auth-2.45.0}/tests/compute_engine/data/smbios_product_name +0 -0
  102. {google_auth-2.44.0 → google_auth-2.45.0}/tests/compute_engine/data/smbios_product_name_non_google +0 -0
  103. {google_auth-2.44.0 → google_auth-2.45.0}/tests/compute_engine/test__mtls.py +0 -0
  104. {google_auth-2.44.0 → google_auth-2.45.0}/tests/conftest.py +0 -0
  105. {google_auth-2.44.0 → google_auth-2.45.0}/tests/crypt/__init__.py +0 -0
  106. {google_auth-2.44.0 → google_auth-2.45.0}/tests/crypt/test__cryptography_rsa.py +0 -0
  107. {google_auth-2.44.0 → google_auth-2.45.0}/tests/crypt/test__python_rsa.py +0 -0
  108. {google_auth-2.44.0 → google_auth-2.45.0}/tests/crypt/test_crypt.py +0 -0
  109. {google_auth-2.44.0 → google_auth-2.45.0}/tests/crypt/test_es.py +0 -0
  110. {google_auth-2.44.0 → google_auth-2.45.0}/tests/crypt/test_es256.py +0 -0
  111. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/authorized_user.json +0 -0
  112. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/authorized_user_cloud_sdk.json +0 -0
  113. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/authorized_user_cloud_sdk_with_quota_project_id.json +0 -0
  114. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/authorized_user_with_rapt_token.json +0 -0
  115. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/client_secrets.json +0 -0
  116. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/context_aware_metadata.json +0 -0
  117. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/enterprise_cert_invalid.json +0 -0
  118. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/enterprise_cert_valid.json +0 -0
  119. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/enterprise_cert_valid_provider.json +0 -0
  120. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es256_privatekey.pem +0 -0
  121. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es256_public_cert.pem +0 -0
  122. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es256_publickey.pem +0 -0
  123. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es256_service_account.json +0 -0
  124. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es384_privatekey.pem +0 -0
  125. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es384_public_cert.pem +0 -0
  126. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es384_publickey.pem +0 -0
  127. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es384_service_account.json +0 -0
  128. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/external_account_authorized_user.json +0 -0
  129. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/external_account_authorized_user_non_gdu.json +0 -0
  130. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/external_subject_token.json +0 -0
  131. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/external_subject_token.txt +0 -0
  132. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/gdch_service_account.json +0 -0
  133. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/impersonated_service_account_authorized_user_source.json +0 -0
  134. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/impersonated_service_account_external_account_authorized_user_source.json +0 -0
  135. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/impersonated_service_account_service_account_source.json +0 -0
  136. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/impersonated_service_account_with_quota_project.json +0 -0
  137. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/old_oauth_credentials_py3.pickle +0 -0
  138. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/other_cert.pem +0 -0
  139. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/pem_from_pkcs12.pem +0 -0
  140. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/privatekey.p12 +0 -0
  141. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/privatekey.pem +0 -0
  142. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/privatekey.pub +0 -0
  143. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/public_cert.pem +0 -0
  144. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/service_account.json +0 -0
  145. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/service_account_non_gdu.json +0 -0
  146. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/trust_chain_with_leaf.pem +0 -0
  147. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/trust_chain_without_leaf.pem +0 -0
  148. {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/trust_chain_wrong_order.pem +0 -0
  149. {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/__init__.py +0 -0
  150. {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test__client.py +0 -0
  151. {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_challenges.py +0 -0
  152. {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_credentials.py +0 -0
  153. {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_gdch_credentials.py +0 -0
  154. {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_id_token.py +0 -0
  155. {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_reauth.py +0 -0
  156. {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_service_account.py +0 -0
  157. {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_sts.py +0 -0
  158. {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_utils.py +0 -0
  159. {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_webauthn_handler.py +0 -0
  160. {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_webauthn_handler_factory.py +0 -0
  161. {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_webauthn_types.py +0 -0
  162. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test__cloud_sdk.py +0 -0
  163. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test__default.py +0 -0
  164. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test__exponential_backoff.py +0 -0
  165. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test__helpers.py +0 -0
  166. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test__oauth2client.py +0 -0
  167. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test__refresh_worker.py +0 -0
  168. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test__service_account_info.py +0 -0
  169. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_api_key.py +0 -0
  170. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_app_engine.py +0 -0
  171. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_aws.py +0 -0
  172. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_credentials.py +0 -0
  173. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_credentials_async.py +0 -0
  174. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_downscoped.py +0 -0
  175. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_exceptions.py +0 -0
  176. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_external_account_authorized_user.py +0 -0
  177. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_iam.py +0 -0
  178. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_impersonated_credentials.py +0 -0
  179. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_jwt.py +0 -0
  180. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_metrics.py +0 -0
  181. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_packaging.py +0 -0
  182. {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_pluggable.py +0 -0
  183. {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/__init__.py +0 -0
  184. {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/aio/test_aiohttp.py +0 -0
  185. {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/aio/test_sessions.py +0 -0
  186. {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/compliance.py +0 -0
  187. {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/test__custom_tls_signer.py +0 -0
  188. {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/test__http_client.py +0 -0
  189. {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/test_grpc.py +0 -0
  190. {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/test_mtls.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: google-auth
3
- Version: 2.44.0
3
+ Version: 2.45.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
@@ -29,6 +29,9 @@ License-File: LICENSE
29
29
  Requires-Dist: cachetools<7.0,>=2.0.0
30
30
  Requires-Dist: pyasn1-modules>=0.2.1
31
31
  Requires-Dist: rsa<5,>=3.1.4
32
+ Provides-Extra: cryptography
33
+ Requires-Dist: cryptography>=38.0.3; extra == "cryptography"
34
+ Requires-Dist: cryptography<39.0.0; python_version < "3.8" and extra == "cryptography"
32
35
  Provides-Extra: aiohttp
33
36
  Requires-Dist: aiohttp<4.0.0,>=3.6.2; extra == "aiohttp"
34
37
  Requires-Dist: requests<3.0.0,>=2.20.0; extra == "aiohttp"
@@ -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
@@ -127,7 +127,7 @@ _CLASS_CONVERSION_MAP = {
127
127
  oauth2client.contrib.gce.AppAssertionCredentials: _convert_gce_app_assertion_credentials,
128
128
  }
129
129
 
130
- if _HAS_APPENGINE:
130
+ if _HAS_APPENGINE: # pragma: no cover
131
131
  _CLASS_CONVERSION_MAP[
132
132
  oauth2client.contrib.appengine.AppAssertionCredentials
133
133
  ] = _convert_appengine_app_assertion_credentials
@@ -451,12 +451,19 @@ def get_service_account_token(request, service_account="default", scopes=None):
451
451
  google.auth.exceptions.TransportError: if an error occurred while
452
452
  retrieving metadata.
453
453
  """
454
+ from google.auth import _agent_identity_utils
455
+
456
+ params = {}
454
457
  if scopes:
455
458
  if not isinstance(scopes, str):
456
459
  scopes = ",".join(scopes)
457
- params = {"scopes": scopes}
458
- else:
459
- params = None
460
+ params["scopes"] = scopes
461
+
462
+ cert = _agent_identity_utils.get_and_parse_agent_identity_certificate()
463
+ if cert:
464
+ if _agent_identity_utils.should_request_bound_token(cert):
465
+ fingerprint = _agent_identity_utils.calculate_certificate_fingerprint(cert)
466
+ params["bindCertificateFingerprint"] = fingerprint
460
467
 
461
468
  metrics_header = {
462
469
  metrics.API_CLIENT_HEADER: metrics.token_request_access_token_mds()
@@ -135,9 +135,9 @@ class Credentials(
135
135
  service can't be reached if if the instance has not
136
136
  credentials.
137
137
  """
138
- scopes = self._scopes if self._scopes is not None else self._default_scopes
139
138
  try:
140
139
  self._retrieve_info(request)
140
+ scopes = self._scopes if self._scopes is not None else self._default_scopes
141
141
  # Always fetch token with default service account email.
142
142
  self.token, self.expiry = _metadata.get_service_account_token(
143
143
  request, service_account="default", scopes=scopes
@@ -92,3 +92,12 @@ AWS_DEFAULT_REGION = "AWS_DEFAULT_REGION"
92
92
  GOOGLE_AUTH_TRUST_BOUNDARY_ENABLED = "GOOGLE_AUTH_TRUST_BOUNDARY_ENABLED"
93
93
  """Environment variable controlling whether to enable trust boundary feature.
94
94
  The default value is false. Users have to explicitly set this value to true."""
95
+
96
+ GOOGLE_API_CERTIFICATE_CONFIG = "GOOGLE_API_CERTIFICATE_CONFIG"
97
+ """Environment variable defining the location of Google API certificate config
98
+ file."""
99
+
100
+ GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES = (
101
+ "GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES"
102
+ )
103
+ """Environment variable to prevent agent token sharing for GCP services."""
@@ -420,6 +420,9 @@ class Credentials(
420
420
  credentials, it will refresh the access token and the trust boundary.
421
421
  """
422
422
  self._refresh_token(request)
423
+ self._handle_trust_boundary(request)
424
+
425
+ def _handle_trust_boundary(self, request):
423
426
  # If we are impersonating, the trust boundary is handled by the
424
427
  # impersonated credentials object. We need to get it from there.
425
428
  if self._service_account_impersonation_url:
@@ -428,7 +431,7 @@ class Credentials(
428
431
  # Otherwise, refresh the trust boundary for the external account.
429
432
  self._refresh_trust_boundary(request)
430
433
 
431
- def _refresh_token(self, request):
434
+ def _refresh_token(self, request, cert_fingerprint=None):
432
435
  scopes = self._scopes if self._scopes is not None else self._default_scopes
433
436
 
434
437
  # Inject client certificate into request.
@@ -446,11 +449,15 @@ class Credentials(
446
449
  self.expiry = self._impersonated_credentials.expiry
447
450
  else:
448
451
  now = _helpers.utcnow()
449
- additional_options = None
452
+ additional_options = {}
450
453
  # Do not pass workforce_pool_user_project when client authentication
451
454
  # is used. The client ID is sufficient for determining the user project.
452
455
  if self._workforce_pool_user_project and not self._client_id:
453
- additional_options = {"userProject": self._workforce_pool_user_project}
456
+ additional_options["userProject"] = self._workforce_pool_user_project
457
+
458
+ if cert_fingerprint:
459
+ additional_options["bindCertFingerprint"] = cert_fingerprint
460
+
454
461
  additional_headers = {
455
462
  metrics.API_CLIENT_HEADER: metrics.byoid_metrics_header(
456
463
  self._metrics_options
@@ -464,7 +471,7 @@ class Credentials(
464
471
  audience=self._audience,
465
472
  scopes=scopes,
466
473
  requested_token_type=_STS_REQUESTED_TOKEN_TYPE,
467
- additional_options=additional_options,
474
+ additional_options=additional_options if additional_options else None,
468
475
  additional_headers=additional_headers,
469
476
  )
470
477
  self.token = response_data.get("access_token")
@@ -550,3 +550,25 @@ class Credentials(external_account.Credentials):
550
550
  credentials.
551
551
  """
552
552
  return super(Credentials, cls).from_file(filename, **kwargs)
553
+
554
+ def refresh(self, request):
555
+ """Refreshes the access token.
556
+
557
+ Args:
558
+ request (google.auth.transport.Request): The object used to make
559
+ HTTP requests.
560
+ """
561
+ from google.auth import _agent_identity_utils
562
+
563
+ cert_fingerprint = None
564
+ # Check if the credential is X.509 based.
565
+ if self._credential_source_certificate is not None:
566
+ cert_bytes = self._get_cert_bytes()
567
+ cert = _agent_identity_utils.parse_certificate(cert_bytes)
568
+ if _agent_identity_utils.should_request_bound_token(cert):
569
+ cert_fingerprint = _agent_identity_utils.calculate_certificate_fingerprint(
570
+ cert
571
+ )
572
+
573
+ self._refresh_token(request, cert_fingerprint=cert_fingerprint)
574
+ self._handle_trust_boundary(request)
@@ -20,11 +20,12 @@ from os import environ, getenv, path
20
20
  import re
21
21
  import subprocess
22
22
 
23
+ from google.auth import _agent_identity_utils
24
+ from google.auth import environment_vars
23
25
  from google.auth import exceptions
24
26
 
25
27
  CONTEXT_AWARE_METADATA_PATH = "~/.secureConnect/context_aware_metadata.json"
26
28
  CERTIFICATE_CONFIGURATION_DEFAULT_PATH = "~/.config/gcloud/certificate_config.json"
27
- _CERTIFICATE_CONFIGURATION_ENV = "GOOGLE_API_CERTIFICATE_CONFIG"
28
29
  _CERT_PROVIDER_COMMAND = "cert_provider_command"
29
30
  _CERT_REGEX = re.compile(
30
31
  b"-----BEGIN CERTIFICATE-----.+-----END CERTIFICATE-----\r?\n?", re.DOTALL
@@ -146,7 +147,7 @@ def _get_cert_config_path(certificate_config_path=None):
146
147
  """
147
148
 
148
149
  if certificate_config_path is None:
149
- env_path = environ.get(_CERTIFICATE_CONFIGURATION_ENV, None)
150
+ env_path = environ.get(environment_vars.GOOGLE_API_CERTIFICATE_CONFIG, None)
150
151
  if env_path is not None and env_path != "":
151
152
  certificate_config_path = env_path
152
153
  else:
@@ -474,3 +475,29 @@ def check_use_client_cert():
474
475
  ) as e:
475
476
  _LOGGER.debug("error decoding certificate: %s", e)
476
477
  return False
478
+
479
+
480
+ def check_parameters_for_unauthorized_response(cached_cert):
481
+ """Returns the cached and current cert fingerprint for reconfiguring mTLS.
482
+
483
+ Args:
484
+ cached_cert(bytes): The cached client certificate.
485
+
486
+ Returns:
487
+ bytes: The client callback cert bytes.
488
+ bytes: The client callback key bytes.
489
+ str: The base64-encoded SHA256 cached fingerprint.
490
+ str: The base64-encoded SHA256 current cert fingerprint.
491
+ """
492
+ call_cert_bytes, call_key_bytes = _agent_identity_utils.call_client_cert_callback()
493
+ cert_obj = _agent_identity_utils.parse_certificate(call_cert_bytes)
494
+ current_cert_fingerprint = _agent_identity_utils.calculate_certificate_fingerprint(
495
+ cert_obj
496
+ )
497
+ if cached_cert:
498
+ cached_fingerprint = _agent_identity_utils.get_cached_cert_fingerprint(
499
+ cached_cert
500
+ )
501
+ else:
502
+ cached_fingerprint = current_cert_fingerprint
503
+ return call_cert_bytes, call_key_bytes, cached_fingerprint, current_cert_fingerprint
@@ -17,6 +17,7 @@
17
17
  from __future__ import absolute_import
18
18
 
19
19
  import functools
20
+ import http.client as http_client
20
21
  import logging
21
22
  import numbers
22
23
  import time
@@ -36,6 +37,7 @@ from requests.packages.urllib3.util.ssl_ import ( # type: ignore
36
37
  from google.auth import _helpers
37
38
  from google.auth import exceptions
38
39
  from google.auth import transport
40
+ from google.auth.transport import _mtls_helper
39
41
  import google.auth.transport._mtls_helper
40
42
  from google.oauth2 import service_account
41
43
 
@@ -463,6 +465,7 @@ class AuthorizedSession(requests.Session):
463
465
 
464
466
  if self._is_mtls:
465
467
  mtls_adapter = _MutualTlsAdapter(cert, key)
468
+ self._cached_cert = cert
466
469
  self.mount("https://", mtls_adapter)
467
470
  except (
468
471
  exceptions.ClientCertError,
@@ -502,6 +505,10 @@ class AuthorizedSession(requests.Session):
502
505
  itself does not timeout, e.g. if a large file is being
503
506
  transmitted. The timout error will be raised after such
504
507
  request completes.
508
+ Raises:
509
+ google.auth.exceptions.MutualTLSChannelError: If mutual TLS
510
+ channel creation fails for any reason.
511
+ ValueError: If the client certificate is invalid.
505
512
  """
506
513
  # pylint: disable=arguments-differ
507
514
  # Requests has a ton of arguments to request, but only two
@@ -551,7 +558,31 @@ class AuthorizedSession(requests.Session):
551
558
  response.status_code in self._refresh_status_codes
552
559
  and _credential_refresh_attempt < self._max_refresh_attempts
553
560
  ):
554
-
561
+ # Handle unauthorized permission error(401 status code)
562
+ if response.status_code == http_client.UNAUTHORIZED:
563
+ if self.is_mtls:
564
+ call_cert_bytes, call_key_bytes, cached_fingerprint, current_cert_fingerprint = _mtls_helper.check_parameters_for_unauthorized_response(
565
+ self._cached_cert
566
+ )
567
+ if cached_fingerprint != current_cert_fingerprint:
568
+ try:
569
+ _LOGGER.info(
570
+ "Client certificate has changed, reconfiguring mTLS "
571
+ "channel."
572
+ )
573
+ self.configure_mtls_channel(
574
+ lambda: (call_cert_bytes, call_key_bytes)
575
+ )
576
+ except Exception as e:
577
+ _LOGGER.error("Failed to reconfigure mTLS channel: %s", e)
578
+ raise exceptions.MutualTLSChannelError(
579
+ "Failed to reconfigure mTLS channel"
580
+ ) from e
581
+ else:
582
+ _LOGGER.info(
583
+ "Skipping reconfiguration of mTLS channel because the client"
584
+ " certificate has not changed."
585
+ )
555
586
  _LOGGER.info(
556
587
  "Refreshing credentials due to a %s response. Attempt %s/%s.",
557
588
  response.status_code,
@@ -16,6 +16,7 @@
16
16
 
17
17
  from __future__ import absolute_import
18
18
 
19
+ import http.client as http_client
19
20
  import logging
20
21
  import warnings
21
22
 
@@ -52,6 +53,7 @@ except ImportError as caught_exc: # pragma: NO COVER
52
53
  from google.auth import _helpers
53
54
  from google.auth import exceptions
54
55
  from google.auth import transport
56
+ from google.auth.transport import _mtls_helper
55
57
  from google.oauth2 import service_account
56
58
 
57
59
  if version.parse(urllib3.__version__) >= version.parse("2.0.0"): # pragma: NO COVER
@@ -299,6 +301,7 @@ class AuthorizedHttp(RequestMethods): # type: ignore
299
301
  # Request instance used by internal methods (for example,
300
302
  # credentials.refresh).
301
303
  self._request = Request(self.http)
304
+ self._is_mtls = False
302
305
 
303
306
  # https://google.aip.dev/auth/4111
304
307
  # Attempt to use self-signed JWTs when a service account is used.
@@ -335,7 +338,10 @@ class AuthorizedHttp(RequestMethods): # type: ignore
335
338
  """
336
339
  use_client_cert = transport._mtls_helper.check_use_client_cert()
337
340
  if not use_client_cert:
341
+ self._is_mtls = False
338
342
  return False
343
+ else:
344
+ self._is_mtls = True
339
345
  try:
340
346
  import OpenSSL
341
347
  except ImportError as caught_exc:
@@ -349,6 +355,7 @@ class AuthorizedHttp(RequestMethods): # type: ignore
349
355
 
350
356
  if found_cert_key:
351
357
  self.http = _make_mutual_tls_http(cert, key)
358
+ self._cached_cert = cert
352
359
  else:
353
360
  self.http = _make_default_http()
354
361
  except (
@@ -381,6 +388,11 @@ class AuthorizedHttp(RequestMethods): # type: ignore
381
388
  if headers is None:
382
389
  headers = self.headers
383
390
 
391
+ use_mtls = False
392
+ if self._is_mtls:
393
+ MTLS_URL_PREFIXES = ["mtls.googleapis.com", "mtls.sandbox.googleapis.com"]
394
+ use_mtls = any([prefix in url for prefix in MTLS_URL_PREFIXES])
395
+
384
396
  # Make a copy of the headers. They will be modified by the credentials
385
397
  # and we want to pass the original headers if we recurse.
386
398
  request_headers = headers.copy()
@@ -402,6 +414,34 @@ class AuthorizedHttp(RequestMethods): # type: ignore
402
414
  response.status in self._refresh_status_codes
403
415
  and _credential_refresh_attempt < self._max_refresh_attempts
404
416
  ):
417
+ if response.status == http_client.UNAUTHORIZED:
418
+ if use_mtls:
419
+ call_cert_bytes, call_key_bytes, cached_fingerprint, current_cert_fingerprint = _mtls_helper.check_parameters_for_unauthorized_response(
420
+ self._cached_cert
421
+ )
422
+ if cached_fingerprint != current_cert_fingerprint:
423
+ try:
424
+ _LOGGER.info(
425
+ "Client certificate has changed, reconfiguring mTLS "
426
+ "channel."
427
+ )
428
+ self.configure_mtls_channel(
429
+ client_cert_callback=lambda: (
430
+ call_cert_bytes,
431
+ call_key_bytes,
432
+ )
433
+ )
434
+ except Exception as e:
435
+ _LOGGER.error("Failed to reconfigure mTLS channel: %s", e)
436
+ raise exceptions.MutualTLSChannelError(
437
+ "Failed to reconfigure mTLS channel"
438
+ ) from e
439
+
440
+ else:
441
+ _LOGGER.info(
442
+ "Skipping reconfiguration of mTLS channel because the "
443
+ "client certificate has not changed."
444
+ )
405
445
 
406
446
  _LOGGER.info(
407
447
  "Refreshing credentials due to a %s response. Attempt %s/%s.",
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- __version__ = "2.44.0"
15
+ __version__ = "2.45.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: google-auth
3
- Version: 2.44.0
3
+ Version: 2.45.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
@@ -29,6 +29,9 @@ License-File: LICENSE
29
29
  Requires-Dist: cachetools<7.0,>=2.0.0
30
30
  Requires-Dist: pyasn1-modules>=0.2.1
31
31
  Requires-Dist: rsa<5,>=3.1.4
32
+ Provides-Extra: cryptography
33
+ Requires-Dist: cryptography>=38.0.3; extra == "cryptography"
34
+ Requires-Dist: cryptography<39.0.0; python_version < "3.8" and extra == "cryptography"
32
35
  Provides-Extra: aiohttp
33
36
  Requires-Dist: aiohttp<4.0.0,>=3.6.2; extra == "aiohttp"
34
37
  Requires-Dist: requests<3.0.0,>=2.20.0; extra == "aiohttp"
@@ -4,6 +4,7 @@ README.rst
4
4
  setup.cfg
5
5
  setup.py
6
6
  google/auth/__init__.py
7
+ google/auth/_agent_identity_utils.py
7
8
  google/auth/_cloud_sdk.py
8
9
  google/auth/_constants.py
9
10
  google/auth/_credentials_async.py
@@ -94,6 +95,7 @@ tests/test__helpers.py
94
95
  tests/test__oauth2client.py
95
96
  tests/test__refresh_worker.py
96
97
  tests/test__service_account_info.py
98
+ tests/test_agent_identity_utils.py
97
99
  tests/test_api_key.py
98
100
  tests/test_app_engine.py
99
101
  tests/test_aws.py