py2docfx 0.1.20rc2196756__py3-none-any.whl → 0.1.21.dev2246704__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 (140) hide show
  1. py2docfx/convert_prepare/get_source.py +1 -1
  2. py2docfx/convert_prepare/package_info.py +37 -27
  3. py2docfx/convert_prepare/tests/test_get_source.py +3 -1
  4. py2docfx/convert_prepare/tests/test_package_info.py +159 -1
  5. py2docfx/docfx_yaml/build_finished.py +1 -1
  6. py2docfx/docfx_yaml/logger.py +42 -28
  7. py2docfx/venv/basevenv/Lib/site-packages/charset_normalizer/api.py +3 -2
  8. py2docfx/venv/basevenv/Lib/site-packages/charset_normalizer/legacy.py +17 -1
  9. py2docfx/venv/basevenv/Lib/site-packages/charset_normalizer/version.py +1 -1
  10. py2docfx/venv/basevenv/Lib/site-packages/requests/__version__.py +2 -2
  11. py2docfx/venv/basevenv/Lib/site-packages/requests/adapters.py +17 -40
  12. py2docfx/venv/basevenv/Lib/site-packages/requests/sessions.py +1 -1
  13. py2docfx/venv/venv1/Lib/site-packages/azure/core/_version.py +1 -1
  14. py2docfx/venv/venv1/Lib/site-packages/azure/core/pipeline/policies/_authentication.py +21 -9
  15. py2docfx/venv/venv1/Lib/site-packages/azure/core/pipeline/policies/_authentication_async.py +21 -9
  16. py2docfx/venv/venv1/Lib/site-packages/azure/core/pipeline/policies/_retry.py +1 -1
  17. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_bearer_token_provider.py +1 -1
  18. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/authorization_code.py +1 -1
  19. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/azd_cli.py +82 -17
  20. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/azure_cli.py +28 -5
  21. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/azure_powershell.py +28 -4
  22. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/broker.py +79 -0
  23. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/chained.py +9 -3
  24. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/default.py +153 -53
  25. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/imds.py +25 -1
  26. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/shared_cache.py +12 -5
  27. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/vscode.py +163 -144
  28. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/workload_identity.py +23 -12
  29. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/__init__.py +4 -0
  30. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/interactive.py +14 -2
  31. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/pipeline.py +4 -2
  32. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/utils.py +96 -0
  33. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_version.py +1 -1
  34. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_bearer_token_provider.py +3 -3
  35. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/authorization_code.py +1 -1
  36. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/azd_cli.py +32 -13
  37. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/azure_cli.py +26 -5
  38. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/azure_powershell.py +13 -2
  39. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/chained.py +1 -1
  40. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/default.py +120 -55
  41. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/imds.py +27 -1
  42. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/on_behalf_of.py +1 -1
  43. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/shared_cache.py +12 -5
  44. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/vscode.py +15 -67
  45. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/workload_identity.py +17 -13
  46. py2docfx/venv/venv1/Lib/site-packages/cffi/__init__.py +2 -2
  47. py2docfx/venv/venv1/Lib/site-packages/cffi/cparser.py +1 -1
  48. py2docfx/venv/venv1/Lib/site-packages/cffi/recompiler.py +5 -5
  49. py2docfx/venv/venv1/Lib/site-packages/cffi/setuptools_ext.py +13 -0
  50. py2docfx/venv/venv1/Lib/site-packages/cffi/vengine_cpy.py +3 -0
  51. py2docfx/venv/venv1/Lib/site-packages/charset_normalizer/api.py +3 -2
  52. py2docfx/venv/venv1/Lib/site-packages/charset_normalizer/legacy.py +17 -1
  53. py2docfx/venv/venv1/Lib/site-packages/charset_normalizer/version.py +1 -1
  54. py2docfx/venv/venv1/Lib/site-packages/cryptography/__about__.py +1 -1
  55. py2docfx/venv/venv1/Lib/site-packages/cryptography/__init__.py +0 -13
  56. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/_oid.py +8 -0
  57. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/asn1/__init__.py +10 -0
  58. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/asn1/asn1.py +116 -0
  59. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py +3 -9
  60. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi +32 -0
  61. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi +23 -0
  62. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi +1 -13
  63. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py +16 -0
  64. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py +16 -1
  65. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py +0 -2
  66. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py +8 -0
  67. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py +0 -47
  68. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py +6 -91
  69. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py +1 -3
  70. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py +1 -1
  71. py2docfx/venv/venv1/Lib/site-packages/cryptography/utils.py +0 -2
  72. py2docfx/venv/venv1/Lib/site-packages/cryptography/x509/name.py +2 -3
  73. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/__init__.py +1 -1
  74. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/any_pb2.py +2 -2
  75. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/api_pb2.py +12 -8
  76. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/compiler/plugin_pb2.py +2 -2
  77. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/descriptor.py +398 -246
  78. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/descriptor_pb2.py +74 -72
  79. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/descriptor_pool.py +5 -4
  80. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/duration_pb2.py +2 -2
  81. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/empty_pb2.py +2 -2
  82. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/field_mask_pb2.py +2 -2
  83. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/api_implementation.py +0 -6
  84. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/extension_dict.py +3 -3
  85. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/field_mask.py +3 -3
  86. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/python_edition_defaults.py +1 -1
  87. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/python_message.py +10 -2
  88. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/type_checkers.py +47 -5
  89. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/json_format.py +55 -32
  90. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/runtime_version.py +6 -26
  91. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/source_context_pb2.py +2 -2
  92. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/struct_pb2.py +2 -2
  93. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/text_format.py +30 -19
  94. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/timestamp_pb2.py +2 -2
  95. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/type_pb2.py +2 -2
  96. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/wrappers_pb2.py +2 -2
  97. py2docfx/venv/venv1/Lib/site-packages/psutil/__init__.py +39 -19
  98. py2docfx/venv/venv1/Lib/site-packages/psutil/_common.py +3 -5
  99. py2docfx/venv/venv1/Lib/site-packages/psutil/_psaix.py +1 -2
  100. py2docfx/venv/venv1/Lib/site-packages/psutil/_psbsd.py +53 -78
  101. py2docfx/venv/venv1/Lib/site-packages/psutil/_pslinux.py +55 -38
  102. py2docfx/venv/venv1/Lib/site-packages/psutil/_psosx.py +40 -12
  103. py2docfx/venv/venv1/Lib/site-packages/psutil/_psposix.py +0 -1
  104. py2docfx/venv/venv1/Lib/site-packages/psutil/_pssunos.py +1 -2
  105. py2docfx/venv/venv1/Lib/site-packages/psutil/_pswindows.py +33 -13
  106. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/__init__.py +185 -122
  107. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/__main__.py +2 -3
  108. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_bsd.py +5 -10
  109. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_connections.py +3 -4
  110. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_contracts.py +41 -45
  111. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_linux.py +35 -38
  112. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_memleaks.py +4 -8
  113. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_misc.py +6 -12
  114. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_osx.py +17 -8
  115. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_posix.py +29 -17
  116. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_process.py +74 -75
  117. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_process_all.py +11 -13
  118. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_scripts.py +2 -3
  119. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_sudo.py +117 -0
  120. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_system.py +21 -31
  121. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_testutils.py +23 -23
  122. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_unicode.py +15 -8
  123. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_windows.py +65 -33
  124. py2docfx/venv/venv1/Lib/site-packages/pycparser/__init__.py +1 -1
  125. py2docfx/venv/venv1/Lib/site-packages/pycparser/c_generator.py +1 -1
  126. py2docfx/venv/venv1/Lib/site-packages/pycparser/c_lexer.py +14 -0
  127. py2docfx/venv/venv1/Lib/site-packages/pycparser/c_parser.py +30 -7
  128. py2docfx/venv/venv1/Lib/site-packages/pycparser/lextab.py +1 -1
  129. py2docfx/venv/venv1/Lib/site-packages/pycparser/yacctab.py +132 -127
  130. py2docfx/venv/venv1/Lib/site-packages/requests/__version__.py +2 -2
  131. py2docfx/venv/venv1/Lib/site-packages/requests/adapters.py +17 -40
  132. py2docfx/venv/venv1/Lib/site-packages/requests/sessions.py +1 -1
  133. py2docfx/venv/venv1/Lib/site-packages/typing_extensions.py +91 -18
  134. {py2docfx-0.1.20rc2196756.dist-info → py2docfx-0.1.21.dev2246704.dist-info}/METADATA +1 -1
  135. {py2docfx-0.1.20rc2196756.dist-info → py2docfx-0.1.21.dev2246704.dist-info}/RECORD +137 -135
  136. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/linux_vscode_adapter.py +0 -100
  137. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/macos_vscode_adapter.py +0 -34
  138. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/win_vscode_adapter.py +0 -77
  139. {py2docfx-0.1.20rc2196756.dist-info → py2docfx-0.1.21.dev2246704.dist-info}/WHEEL +0 -0
  140. {py2docfx-0.1.20rc2196756.dist-info → py2docfx-0.1.21.dev2246704.dist-info}/top_level.txt +0 -0
@@ -21,10 +21,12 @@ from ..._credentials.azd_cli import (
21
21
  EXECUTABLE_NAME,
22
22
  get_safe_working_dir,
23
23
  NOT_LOGGED_IN,
24
+ UNKNOWN_CLAIMS_FLAG,
24
25
  parse_token,
25
26
  sanitize_output,
27
+ extract_cli_error_message,
26
28
  )
27
- from ..._internal import resolve_tenant, within_dac, validate_tenant_id, validate_scope
29
+ from ..._internal import encode_base64, resolve_tenant, within_dac, validate_tenant_id, validate_scope
28
30
 
29
31
 
30
32
  _LOGGER = logging.getLogger(__name__)
@@ -87,7 +89,7 @@ class AzureDeveloperCliCredential(AsyncContextManager):
87
89
  async def get_token(
88
90
  self,
89
91
  *scopes: str,
90
- claims: Optional[str] = None, # pylint:disable=unused-argument
92
+ claims: Optional[str] = None,
91
93
  tenant_id: Optional[str] = None,
92
94
  **kwargs: Any,
93
95
  ) -> AccessToken:
@@ -99,7 +101,8 @@ class AzureDeveloperCliCredential(AsyncContextManager):
99
101
  :param str scopes: desired scope for the access token. This credential allows only one scope per request.
100
102
  For more information about scopes, see
101
103
  https://learn.microsoft.com/entra/identity-platform/scopes-oidc.
102
- :keyword str claims: not used by this credential; any value provided will be ignored.
104
+ :keyword str claims: additional claims required in the token, such as those returned in a resource provider's
105
+ claims challenge following an authorization failure.
103
106
  :keyword str tenant_id: optional tenant to include in the token request.
104
107
 
105
108
  :return: An access token with the desired scopes.
@@ -110,11 +113,13 @@ class AzureDeveloperCliCredential(AsyncContextManager):
110
113
  """
111
114
  # only ProactorEventLoop supports subprocesses on Windows (and it isn't the default loop on Python < 3.8)
112
115
  if sys.platform.startswith("win") and not isinstance(asyncio.get_event_loop(), asyncio.ProactorEventLoop):
113
- return _SyncAzureDeveloperCliCredential().get_token(*scopes, tenant_id=tenant_id, **kwargs)
116
+ return _SyncAzureDeveloperCliCredential().get_token(*scopes, claims=claims, tenant_id=tenant_id, **kwargs)
114
117
 
115
118
  options: TokenRequestOptions = {}
116
119
  if tenant_id:
117
120
  options["tenant_id"] = tenant_id
121
+ if claims:
122
+ options["claims"] = claims
118
123
 
119
124
  token_info = await self._get_token_base(*scopes, options=options, **kwargs)
120
125
  return AccessToken(token_info.token, token_info.expires_on)
@@ -152,6 +157,7 @@ class AzureDeveloperCliCredential(AsyncContextManager):
152
157
  raise ValueError("Missing scope in request. \n")
153
158
 
154
159
  tenant_id = options.get("tenant_id") if options else None
160
+ claims = options.get("claims") if options else None
155
161
  if tenant_id:
156
162
  validate_tenant_id(tenant_id)
157
163
  for scope in scopes:
@@ -169,16 +175,22 @@ class AzureDeveloperCliCredential(AsyncContextManager):
169
175
 
170
176
  if tenant:
171
177
  command_args += ["--tenant-id", tenant]
178
+ if claims:
179
+ command_args += ["--claims", encode_base64(claims)]
172
180
  output = await _run_command(command_args, self._process_timeout)
173
181
 
174
182
  token = parse_token(output)
175
183
  if not token:
176
- sanitized_output = sanitize_output(output)
177
- message = (
178
- f"Unexpected output from Azure Developer CLI: '{sanitized_output}'. \n"
179
- f"To mitigate this issue, please refer to the troubleshooting guidelines here at "
180
- f"https://aka.ms/azsdk/python/identity/azdevclicredential/troubleshoot."
181
- )
184
+ extracted = extract_cli_error_message(output)
185
+ if extracted:
186
+ message = extracted
187
+ else:
188
+ sanitized_output = sanitize_output(output)
189
+ message = (
190
+ f"Unexpected output from Azure Developer CLI: '{sanitized_output}'. \n"
191
+ f"To mitigate this issue, please refer to the troubleshooting guidelines here at "
192
+ f"https://aka.ms/azsdk/python/identity/azdevclicredential/troubleshoot."
193
+ )
182
194
  if within_dac.get():
183
195
  raise CredentialUnavailableError(message=message)
184
196
  raise ClientAuthenticationError(message=message)
@@ -226,10 +238,17 @@ async def _run_command(command_args: List[str], timeout: int) -> str:
226
238
  if proc.returncode == 127 or stderr.startswith("'azd' is not recognized"):
227
239
  raise CredentialUnavailableError(CLI_NOT_FOUND)
228
240
 
229
- if "not logged in, run `azd auth login` to login" in stderr and "AADSTS" not in stderr:
241
+ combined_text = f"{output}\n{stderr}"
242
+ if "not logged in, run `azd auth login` to login" in combined_text and "AADSTS" not in combined_text:
230
243
  raise CredentialUnavailableError(message=NOT_LOGGED_IN)
231
-
232
- message = sanitize_output(stderr) if stderr else "Failed to invoke Azure Developer CLI"
244
+ if "unknown flag: --claims" in combined_text:
245
+ raise CredentialUnavailableError(message=UNKNOWN_CLAIMS_FLAG)
246
+
247
+ message = (
248
+ extract_cli_error_message(output)
249
+ or extract_cli_error_message(stderr)
250
+ or (sanitize_output(stderr) if stderr else "Failed to invoke Azure Developer CLI")
251
+ )
233
252
  if within_dac.get():
234
253
  raise CredentialUnavailableError(message=message)
235
254
  raise ClientAuthenticationError(message=message)
@@ -23,9 +23,11 @@ from ..._credentials.azure_cli import (
23
23
  NOT_LOGGED_IN,
24
24
  parse_token,
25
25
  sanitize_output,
26
+ CLAIMS_UNSUPPORTED_ERROR,
26
27
  )
27
28
  from ..._internal import (
28
29
  _scopes_to_resource,
30
+ encode_base64,
29
31
  resolve_tenant,
30
32
  within_dac,
31
33
  validate_tenant_id,
@@ -82,7 +84,7 @@ class AzureCliCredential(AsyncContextManager):
82
84
  async def get_token(
83
85
  self,
84
86
  *scopes: str,
85
- claims: Optional[str] = None, # pylint:disable=unused-argument
87
+ claims: Optional[str] = None,
86
88
  tenant_id: Optional[str] = None,
87
89
  **kwargs: Any,
88
90
  ) -> AccessToken:
@@ -94,22 +96,26 @@ class AzureCliCredential(AsyncContextManager):
94
96
  :param str scopes: desired scope for the access token. This credential allows only one scope per request.
95
97
  For more information about scopes, see
96
98
  https://learn.microsoft.com/entra/identity-platform/scopes-oidc.
97
- :keyword str claims: not used by this credential; any value provided will be ignored.
99
+ :keyword str claims: additional claims required in the token. This credential does not support claims
100
+ challenges.
98
101
  :keyword str tenant_id: optional tenant to include in the token request.
99
102
 
100
103
  :return: An access token with the desired scopes.
101
104
  :rtype: ~azure.core.credentials.AccessToken
102
- :raises ~azure.identity.CredentialUnavailableError: the credential was unable to invoke the Azure CLI.
105
+ :raises ~azure.identity.CredentialUnavailableError: the credential was either unable to invoke the Azure CLI
106
+ or a claims challenge was provided.
103
107
  :raises ~azure.core.exceptions.ClientAuthenticationError: the credential invoked the Azure CLI but didn't
104
108
  receive an access token.
105
109
  """
106
110
  # only ProactorEventLoop supports subprocesses on Windows (and it isn't the default loop on Python < 3.8)
107
111
  if sys.platform.startswith("win") and not isinstance(asyncio.get_event_loop(), asyncio.ProactorEventLoop):
108
- return _SyncAzureCliCredential().get_token(*scopes, tenant_id=tenant_id, **kwargs)
112
+ return _SyncAzureCliCredential().get_token(*scopes, claims=claims, tenant_id=tenant_id, **kwargs)
109
113
 
110
114
  options: TokenRequestOptions = {}
111
115
  if tenant_id:
112
116
  options["tenant_id"] = tenant_id
117
+ if claims:
118
+ options["claims"] = claims
113
119
 
114
120
  token_info = await self._get_token_base(*scopes, options=options, **kwargs)
115
121
  return AccessToken(token_info.token, token_info.expires_on)
@@ -130,7 +136,8 @@ class AzureCliCredential(AsyncContextManager):
130
136
  :rtype: ~azure.core.credentials.AccessTokenInfo
131
137
  :return: An AccessTokenInfo instance containing information about the token.
132
138
 
133
- :raises ~azure.identity.CredentialUnavailableError: the credential was unable to invoke the Azure CLI.
139
+ :raises ~azure.identity.CredentialUnavailableError: the credential was either unable to invoke the Azure CLI
140
+ or a claims challenge was provided.
134
141
  :raises ~azure.core.exceptions.ClientAuthenticationError: the credential invoked the Azure CLI but didn't
135
142
  receive an access token.
136
143
  """
@@ -142,6 +149,20 @@ class AzureCliCredential(AsyncContextManager):
142
149
  async def _get_token_base(
143
150
  self, *scopes: str, options: Optional[TokenRequestOptions] = None, **kwargs: Any
144
151
  ) -> AccessTokenInfo:
152
+ # Check for claims challenge first
153
+ if options and "claims" in options and options["claims"]:
154
+ error_message = CLAIMS_UNSUPPORTED_ERROR.format(claims_value=encode_base64(options["claims"]))
155
+
156
+ # Add tenant if provided in options
157
+ if options.get("tenant_id"):
158
+ error_message += f" --tenant {options.get('tenant_id')}"
159
+
160
+ # Add scope if provided
161
+ if scopes:
162
+ error_message += f" --scope {scopes[0]}"
163
+
164
+ raise CredentialUnavailableError(message=error_message)
165
+
145
166
  tenant_id = options.get("tenant_id") if options else None
146
167
  if tenant_id:
147
168
  validate_tenant_id(tenant_id)
@@ -16,8 +16,9 @@ from ..._credentials.azure_powershell import (
16
16
  get_safe_working_dir,
17
17
  raise_for_error,
18
18
  parse_token,
19
+ CLAIMS_UNSUPPORTED_ERROR,
19
20
  )
20
- from ..._internal import resolve_tenant, validate_tenant_id, validate_scope
21
+ from ..._internal import encode_base64, resolve_tenant, validate_tenant_id, validate_scope
21
22
 
22
23
 
23
24
  class AzurePowerShellCredential(AsyncContextManager):
@@ -58,7 +59,7 @@ class AzurePowerShellCredential(AsyncContextManager):
58
59
  async def get_token(
59
60
  self,
60
61
  *scopes: str,
61
- claims: Optional[str] = None, # pylint:disable=unused-argument
62
+ claims: Optional[str] = None,
62
63
  tenant_id: Optional[str] = None,
63
64
  **kwargs: Any,
64
65
  ) -> AccessToken:
@@ -87,6 +88,8 @@ class AzurePowerShellCredential(AsyncContextManager):
87
88
  options: TokenRequestOptions = {}
88
89
  if tenant_id:
89
90
  options["tenant_id"] = tenant_id
91
+ if claims:
92
+ options["claims"] = claims
90
93
 
91
94
  token_info = await self._get_token_base(*scopes, options=options, **kwargs)
92
95
  return AccessToken(token_info.token, token_info.expires_on)
@@ -119,6 +122,14 @@ class AzurePowerShellCredential(AsyncContextManager):
119
122
  async def _get_token_base(
120
123
  self, *scopes: str, options: Optional[TokenRequestOptions] = None, **kwargs: Any
121
124
  ) -> AccessTokenInfo:
125
+
126
+ # Check if claims challenge is provided
127
+ if options and "claims" in options and options["claims"]:
128
+ error_message = CLAIMS_UNSUPPORTED_ERROR.format(claims_value=encode_base64(options["claims"]))
129
+ if options.get("tenant_id"):
130
+ error_message += f" -Tenant {options.get('tenant_id')}"
131
+ raise CredentialUnavailableError(message=error_message)
132
+
122
133
  tenant_id = options.get("tenant_id") if options else None
123
134
  if tenant_id:
124
135
  validate_tenant_id(tenant_id)
@@ -25,7 +25,7 @@ class ChainedTokenCredential(AsyncContextManager):
25
25
  https://aka.ms/azsdk/python/identity/credential-chains#chainedtokencredential-overview.
26
26
 
27
27
  :param credentials: credential instances to form the chain
28
- :type credentials: ~azure.core.credentials_async.AsyncTokenCredential
28
+ :type credentials: ~azure.core.credentials_async.AsyncTokenProvider
29
29
 
30
30
  .. admonition:: Example:
31
31
 
@@ -8,8 +8,9 @@ from typing import List, Optional, Any, cast
8
8
 
9
9
  from azure.core.credentials import AccessToken, AccessTokenInfo, TokenRequestOptions
10
10
  from azure.core.credentials_async import AsyncTokenCredential, AsyncSupportsTokenInfo
11
+ from ... import CredentialUnavailableError
11
12
  from ..._constants import EnvironmentVariables
12
- from ..._internal import get_default_authority, normalize_authority, within_dac
13
+ from ..._internal import get_default_authority, normalize_authority, within_dac, process_credential_exclusions
13
14
  from .azure_cli import AzureCliCredential
14
15
  from .azd_cli import AzureDeveloperCliCredential
15
16
  from .azure_powershell import AzurePowerShellCredential
@@ -24,6 +25,34 @@ from .workload_identity import WorkloadIdentityCredential
24
25
  _LOGGER = logging.getLogger(__name__)
25
26
 
26
27
 
28
+ class AsyncFailedDACCredential:
29
+ """Async version of FailedDACCredential for use in async credential chains.
30
+
31
+ This acts as a substitute for an async credential that has failed to initialize in the DAC chain.
32
+ """
33
+
34
+ def __init__(self, credential_name: str, error: str) -> None:
35
+ self._error = error
36
+ self._credential_name = credential_name
37
+
38
+ async def get_token(self, *scopes: str, **kwargs: Any) -> AccessToken:
39
+ raise CredentialUnavailableError(self._error)
40
+
41
+ async def get_token_info(
42
+ self, *scopes, options: Optional[TokenRequestOptions] = None, **kwargs: Any
43
+ ) -> AccessTokenInfo:
44
+ raise CredentialUnavailableError(self._error)
45
+
46
+ async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
47
+ pass
48
+
49
+ async def __aenter__(self) -> "AsyncFailedDACCredential":
50
+ return self
51
+
52
+ async def close(self) -> None:
53
+ pass
54
+
55
+
27
56
  class DefaultAzureCredential(ChainedTokenCredential):
28
57
  """A credential capable of handling most Azure SDK authentication scenarios. See
29
58
  https://aka.ms/azsdk/python/identity/credential-chains#usage-guidance-for-defaultazurecredential.
@@ -73,11 +102,13 @@ class DefaultAzureCredential(ChainedTokenCredential):
73
102
  :keyword str shared_cache_tenant_id: Preferred tenant for :class:`~azure.identity.aio.SharedTokenCacheCredential`.
74
103
  Defaults to the value of environment variable AZURE_TENANT_ID, if any.
75
104
  :keyword str visual_studio_code_tenant_id: Tenant ID to use when authenticating with
76
- :class:`~azure.identity.aio.VisualStudioCodeCredential`. Defaults to the "Azure: Tenant" setting in VS Code's
77
- user settings or, when that setting has no value, the "organizations" tenant, which supports only Azure Active
78
- Directory work or school accounts.
105
+ :class:`~azure.identity.VisualStudioCodeCredential`. Defaults to the tenant specified in the authentication
106
+ record file used by the Azure Resources extension.
79
107
  :keyword int process_timeout: The timeout in seconds to use for developer credentials that run
80
108
  subprocesses (e.g. AzureCliCredential, AzurePowerShellCredential). Defaults to **10** seconds.
109
+ :keyword bool require_envvar: If **True**, require that the AZURE_TOKEN_CREDENTIALS environment variable be set
110
+ to a value denoting the credential type or credential group to use. If unset or empty, DefaultAzureCredential
111
+ will raise a `ValueError`. Defaults to **False**.
81
112
 
82
113
  .. admonition:: Example:
83
114
 
@@ -89,23 +120,15 @@ class DefaultAzureCredential(ChainedTokenCredential):
89
120
  :caption: Create a DefaultAzureCredential.
90
121
  """
91
122
 
92
- def __init__(self, **kwargs: Any) -> None: # pylint: disable=too-many-statements
123
+ def __init__(self, **kwargs: Any) -> None: # pylint: disable=too-many-statements, too-many-locals
93
124
  if "tenant_id" in kwargs:
94
125
  raise TypeError("'tenant_id' is not supported in DefaultAzureCredential.")
95
126
 
96
127
  authority = kwargs.pop("authority", None)
97
-
98
- vscode_tenant_id = kwargs.pop(
99
- "visual_studio_code_tenant_id", os.environ.get(EnvironmentVariables.AZURE_TENANT_ID)
100
- )
101
- vscode_args = dict(kwargs)
102
- if authority:
103
- vscode_args["authority"] = authority
104
- if vscode_tenant_id:
105
- vscode_args["tenant_id"] = vscode_tenant_id
106
-
107
128
  authority = normalize_authority(authority) if authority else get_default_authority()
108
129
 
130
+ vscode_tenant_id = kwargs.pop("visual_studio_code_tenant_id", None)
131
+
109
132
  shared_cache_username = kwargs.pop("shared_cache_username", os.environ.get(EnvironmentVariables.AZURE_USERNAME))
110
133
  shared_cache_tenant_id = kwargs.pop(
111
134
  "shared_cache_tenant_id", os.environ.get(EnvironmentVariables.AZURE_TENANT_ID)
@@ -119,56 +142,94 @@ class DefaultAzureCredential(ChainedTokenCredential):
119
142
  "workload_identity_tenant_id", os.environ.get(EnvironmentVariables.AZURE_TENANT_ID)
120
143
  )
121
144
 
122
- vscode_tenant_id = kwargs.pop(
123
- "visual_studio_code_tenant_id", os.environ.get(EnvironmentVariables.AZURE_TENANT_ID)
124
- )
125
-
126
145
  process_timeout = kwargs.pop("process_timeout", 10)
127
-
128
- token_credentials_env = os.environ.get(EnvironmentVariables.AZURE_TOKEN_CREDENTIALS, "").strip().lower()
129
- exclude_workload_identity_credential = kwargs.pop("exclude_workload_identity_credential", False)
130
- exclude_visual_studio_code_credential = kwargs.pop("exclude_visual_studio_code_credential", True)
131
- exclude_developer_cli_credential = kwargs.pop("exclude_developer_cli_credential", False)
132
- exclude_cli_credential = kwargs.pop("exclude_cli_credential", False)
133
- exclude_environment_credential = kwargs.pop("exclude_environment_credential", False)
134
- exclude_managed_identity_credential = kwargs.pop("exclude_managed_identity_credential", False)
135
- exclude_shared_token_cache_credential = kwargs.pop("exclude_shared_token_cache_credential", False)
136
- exclude_powershell_credential = kwargs.pop("exclude_powershell_credential", False)
137
-
138
- if token_credentials_env == "dev":
139
- # In dev mode, use only developer credentials
140
- exclude_environment_credential = True
141
- exclude_managed_identity_credential = True
142
- exclude_workload_identity_credential = True
143
- elif token_credentials_env == "prod":
144
- # In prod mode, use only production credentials
145
- exclude_shared_token_cache_credential = True
146
- exclude_visual_studio_code_credential = True
147
- exclude_cli_credential = True
148
- exclude_developer_cli_credential = True
149
- exclude_powershell_credential = True
150
- elif token_credentials_env != "":
151
- # If the environment variable is set to something other than dev or prod, raise an error
146
+ require_envvar = kwargs.pop("require_envvar", False)
147
+ if require_envvar and not os.environ.get(EnvironmentVariables.AZURE_TOKEN_CREDENTIALS):
152
148
  raise ValueError(
153
- f"Invalid value for {EnvironmentVariables.AZURE_TOKEN_CREDENTIALS}: {token_credentials_env}. "
154
- "Valid values are 'dev' or 'prod'."
149
+ "AZURE_TOKEN_CREDENTIALS environment variable is required but is not set or is empty. "
150
+ "Set it to 'dev', 'prod', or a specific credential name."
155
151
  )
156
152
 
153
+ # Define credential configuration mapping (async version)
154
+ credential_config = {
155
+ "environment": {
156
+ "exclude_param": "exclude_environment_credential",
157
+ "env_name": "environmentcredential",
158
+ "default_exclude": False,
159
+ },
160
+ "workload_identity": {
161
+ "exclude_param": "exclude_workload_identity_credential",
162
+ "env_name": "workloadidentitycredential",
163
+ "default_exclude": False,
164
+ },
165
+ "managed_identity": {
166
+ "exclude_param": "exclude_managed_identity_credential",
167
+ "env_name": "managedidentitycredential",
168
+ "default_exclude": False,
169
+ },
170
+ "shared_token_cache": {
171
+ "exclude_param": "exclude_shared_token_cache_credential",
172
+ "default_exclude": False,
173
+ },
174
+ "visual_studio_code": {
175
+ "exclude_param": "exclude_visual_studio_code_credential",
176
+ "env_name": "visualstudiocodecredential",
177
+ "default_exclude": False,
178
+ },
179
+ "cli": {
180
+ "exclude_param": "exclude_cli_credential",
181
+ "env_name": "azureclicredential",
182
+ "default_exclude": False,
183
+ },
184
+ "developer_cli": {
185
+ "exclude_param": "exclude_developer_cli_credential",
186
+ "env_name": "azuredeveloperclicredential",
187
+ "default_exclude": False,
188
+ },
189
+ "powershell": {
190
+ "exclude_param": "exclude_powershell_credential",
191
+ "env_name": "azurepowershellcredential",
192
+ "default_exclude": False,
193
+ },
194
+ }
195
+
196
+ # Extract user-provided exclude flags and set defaults
197
+ exclude_flags = {}
198
+ user_excludes = {}
199
+ for cred_key, config in credential_config.items():
200
+ param_name = cast(str, config["exclude_param"])
201
+ user_excludes[cred_key] = kwargs.pop(param_name, None)
202
+ exclude_flags[cred_key] = config["default_exclude"]
203
+
204
+ # Process AZURE_TOKEN_CREDENTIALS environment variable and apply user overrides
205
+ exclude_flags = process_credential_exclusions(credential_config, exclude_flags, user_excludes)
206
+
207
+ # Extract individual exclude flags for backward compatibility
208
+ exclude_environment_credential = exclude_flags["environment"]
209
+ exclude_workload_identity_credential = exclude_flags["workload_identity"]
210
+ exclude_managed_identity_credential = exclude_flags["managed_identity"]
211
+ exclude_shared_token_cache_credential = exclude_flags["shared_token_cache"]
212
+ exclude_visual_studio_code_credential = exclude_flags["visual_studio_code"]
213
+ exclude_cli_credential = exclude_flags["cli"]
214
+ exclude_developer_cli_credential = exclude_flags["developer_cli"]
215
+ exclude_powershell_credential = exclude_flags["powershell"]
216
+
157
217
  credentials: List[AsyncSupportsTokenInfo] = []
158
218
  within_dac.set(True)
159
219
  if not exclude_environment_credential:
160
220
  credentials.append(EnvironmentCredential(authority=authority, _within_dac=True, **kwargs))
161
221
  if not exclude_workload_identity_credential:
162
- if all(os.environ.get(var) for var in EnvironmentVariables.WORKLOAD_IDENTITY_VARS):
163
- client_id = workload_identity_client_id
222
+ try:
164
223
  credentials.append(
165
224
  WorkloadIdentityCredential(
166
- client_id=cast(str, client_id),
225
+ client_id=cast(str, workload_identity_client_id),
167
226
  tenant_id=workload_identity_tenant_id,
168
- token_file_path=os.environ[EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE],
227
+ token_file_path=os.environ.get(EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE),
169
228
  **kwargs,
170
229
  )
171
230
  )
231
+ except ValueError as ex:
232
+ credentials.append(AsyncFailedDACCredential("WorkloadIdentityCredential", error=str(ex)))
172
233
  if not exclude_managed_identity_credential:
173
234
  credentials.append(
174
235
  ManagedIdentityCredential(
@@ -187,7 +248,7 @@ class DefaultAzureCredential(ChainedTokenCredential):
187
248
  except Exception as ex: # pylint:disable=broad-except
188
249
  _LOGGER.info("Shared token cache is unavailable: '%s'", ex)
189
250
  if not exclude_visual_studio_code_credential:
190
- credentials.append(VisualStudioCodeCredential(**vscode_args))
251
+ credentials.append(VisualStudioCodeCredential(tenant_id=vscode_tenant_id))
191
252
  if not exclude_cli_credential:
192
253
  credentials.append(AzureCliCredential(process_timeout=process_timeout))
193
254
  if not exclude_powershell_credential:
@@ -226,8 +287,10 @@ class DefaultAzureCredential(ChainedTokenCredential):
226
287
  return token
227
288
 
228
289
  within_dac.set(True)
229
- token = await super().get_token(*scopes, claims=claims, tenant_id=tenant_id, **kwargs)
230
- within_dac.set(False)
290
+ try:
291
+ token = await super().get_token(*scopes, claims=claims, tenant_id=tenant_id, **kwargs)
292
+ finally:
293
+ within_dac.set(False)
231
294
  return token
232
295
 
233
296
  async def get_token_info(self, *scopes: str, options: Optional[TokenRequestOptions] = None) -> AccessTokenInfo:
@@ -257,6 +320,8 @@ class DefaultAzureCredential(ChainedTokenCredential):
257
320
  return token_info
258
321
 
259
322
  within_dac.set(True)
260
- token_info = await cast(AsyncSupportsTokenInfo, super()).get_token_info(*scopes, options=options)
261
- within_dac.set(False)
323
+ try:
324
+ token_info = await cast(AsyncSupportsTokenInfo, super()).get_token_info(*scopes, options=options)
325
+ finally:
326
+ within_dac.set(False)
262
327
  return token_info
@@ -3,10 +3,13 @@
3
3
  # Licensed under the MIT License.
4
4
  # ------------------------------------
5
5
  import os
6
- from typing import Optional, Any
6
+ from typing import Optional, Any, Dict
7
7
 
8
8
  from azure.core.exceptions import ClientAuthenticationError, HttpResponseError
9
9
  from azure.core.credentials import AccessTokenInfo
10
+ from azure.core.pipeline.policies import AsyncRetryPolicy
11
+ from azure.core.pipeline import PipelineResponse
12
+
10
13
  from ... import CredentialUnavailableError
11
14
  from ..._constants import EnvironmentVariables
12
15
  from .._internal import AsyncContextManager
@@ -16,10 +19,33 @@ from ..._internal import within_credential_chain
16
19
  from ..._credentials.imds import _get_request, _check_forbidden_response, PIPELINE_SETTINGS
17
20
 
18
21
 
22
+ class AsyncImdsRetryPolicy(AsyncRetryPolicy):
23
+ """Async custom retry policy for IMDS credential with extended retry duration for 410 responses.
24
+
25
+ This policy ensures that specifically for 410 status codes, the total exponential backoff duration
26
+ is at least 70 seconds to handle temporary IMDS endpoint unavailability.
27
+ For other status codes, it uses the standard retry behavior.
28
+ """
29
+
30
+ def __init__(self, **kwargs: Any) -> None:
31
+ # Increased backoff factor to ensure at least 70 seconds retry duration for 410 responses.
32
+ # Five retries, with each retry sleeping for [0.0s, 5.0s, 10.0s, 20.0s, 40.0s] between attempts (75s total)
33
+ self.backoff_factor_for_410 = 2.5
34
+ super().__init__(**kwargs)
35
+
36
+ def is_retry(self, settings: Dict[str, Any], response: PipelineResponse[Any, Any]) -> bool:
37
+ if response.http_response.status_code == 410:
38
+ settings["backoff"] = self.backoff_factor_for_410
39
+ else:
40
+ settings["backoff"] = self.backoff_factor
41
+ return super().is_retry(settings, response)
42
+
43
+
19
44
  class ImdsCredential(AsyncContextManager, GetTokenMixin):
20
45
  def __init__(self, **kwargs: Any) -> None:
21
46
  super().__init__()
22
47
 
48
+ kwargs["retry_policy_class"] = AsyncImdsRetryPolicy
23
49
  self._client = AsyncManagedIdentityClient(_get_request, **dict(PIPELINE_SETTINGS, **kwargs))
24
50
  if EnvironmentVariables.AZURE_POD_IDENTITY_AUTHORITY_HOST in os.environ:
25
51
  self._endpoint_available: Optional[bool] = True
@@ -119,7 +119,7 @@ class OnBehalfOfCredential(AsyncContextManager, GetTokenMixin):
119
119
  # Note we assume the cache has tokens for one user only. That's okay because each instance of this class is
120
120
  # locked to a single user (assertion). This assumption will become unsafe if this class allows applications
121
121
  # to change an instance's assertion.
122
- refresh_tokens = self._client.get_cached_refresh_tokens(scopes)
122
+ refresh_tokens = self._client.get_cached_refresh_tokens(scopes, **kwargs)
123
123
  if len(refresh_tokens) == 1: # there should be only one
124
124
  try:
125
125
  refresh_token = refresh_tokens[0]["secret"]
@@ -8,6 +8,7 @@ from ..._internal.aad_client import AadClientBase
8
8
  from ... import CredentialUnavailableError
9
9
  from ..._constants import DEVELOPER_SIGN_ON_CLIENT_ID
10
10
  from ..._internal.shared_token_cache import NO_TOKEN, SharedTokenCacheBase
11
+ from ..._internal.utils import within_dac
11
12
  from .._internal import AsyncContextManager
12
13
  from .._internal.aad_client import AadClient
13
14
  from .._internal.decorators import log_get_token_async
@@ -143,11 +144,17 @@ class SharedTokenCacheCredential(SharedTokenCacheBase, AsyncContextManager):
143
144
 
144
145
  # try each refresh token, returning the first access token acquired
145
146
  for refresh_token in self._get_refresh_tokens(account, is_cae=is_cae):
146
- token = await cast(AadClient, self._client).obtain_token_by_refresh_token(
147
- scopes, refresh_token, claims=claims, tenant_id=tenant_id, enable_cae=is_cae, **kwargs
148
- )
149
- return token
150
-
147
+ try:
148
+ token = await cast(AadClient, self._client).obtain_token_by_refresh_token(
149
+ scopes, refresh_token, claims=claims, tenant_id=tenant_id, enable_cae=is_cae, **kwargs
150
+ )
151
+ return token
152
+ except Exception as e: # pylint: disable=broad-except
153
+ if within_dac.get():
154
+ raise CredentialUnavailableError( # pylint: disable=raise-missing-from
155
+ message=getattr(e, "message", str(e)), response=getattr(e, "response", None)
156
+ )
157
+ raise
151
158
  raise CredentialUnavailableError(message=NO_TOKEN.format(account.get("username")))
152
159
 
153
160
  def _get_auth_client(self, **kwargs: Any) -> AadClientBase: