py2docfx 0.1.19rc2173760__py3-none-any.whl → 0.1.20__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 (39) hide show
  1. py2docfx/__main__.py +2 -2
  2. py2docfx/convert_prepare/arg_parser.py +1 -1
  3. py2docfx/convert_prepare/get_source.py +40 -8
  4. py2docfx/convert_prepare/git.py +1 -1
  5. py2docfx/docfx_yaml/logger.py +23 -19
  6. py2docfx/venv/basevenv/Lib/site-packages/certifi/__init__.py +1 -1
  7. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/authorization_code.py +1 -1
  8. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/broker.py +79 -0
  9. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/chained.py +8 -2
  10. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/default.py +145 -54
  11. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/imds.py +25 -1
  12. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/vscode.py +163 -144
  13. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/workload_identity.py +23 -12
  14. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/__init__.py +2 -0
  15. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/pipeline.py +4 -2
  16. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/utils.py +85 -0
  17. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_version.py +1 -1
  18. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/authorization_code.py +1 -1
  19. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/default.py +112 -56
  20. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/imds.py +27 -1
  21. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/on_behalf_of.py +1 -1
  22. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/vscode.py +15 -67
  23. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/workload_identity.py +17 -13
  24. py2docfx/venv/venv1/Lib/site-packages/certifi/__init__.py +1 -1
  25. py2docfx/venv/venv1/Lib/site-packages/cryptography/__about__.py +1 -1
  26. py2docfx/venv/venv1/Lib/site-packages/msal/__main__.py +2 -0
  27. py2docfx/venv/venv1/Lib/site-packages/msal/application.py +63 -21
  28. py2docfx/venv/venv1/Lib/site-packages/msal/broker.py +5 -2
  29. py2docfx/venv/venv1/Lib/site-packages/msal/exceptions.py +19 -5
  30. py2docfx/venv/venv1/Lib/site-packages/msal/managed_identity.py +50 -10
  31. py2docfx/venv/venv1/Lib/site-packages/msal/sku.py +1 -1
  32. py2docfx/venv/venv1/Lib/site-packages/msal/throttled_http_client.py +4 -1
  33. {py2docfx-0.1.19rc2173760.dist-info → py2docfx-0.1.20.dist-info}/METADATA +1 -1
  34. {py2docfx-0.1.19rc2173760.dist-info → py2docfx-0.1.20.dist-info}/RECORD +36 -38
  35. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/linux_vscode_adapter.py +0 -100
  36. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/macos_vscode_adapter.py +0 -34
  37. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/win_vscode_adapter.py +0 -77
  38. {py2docfx-0.1.19rc2173760.dist-info → py2docfx-0.1.20.dist-info}/WHEEL +0 -0
  39. {py2docfx-0.1.19rc2173760.dist-info → py2docfx-0.1.20.dist-info}/top_level.txt +0 -0
@@ -2,149 +2,190 @@
2
2
  # Copyright (c) Microsoft Corporation.
3
3
  # Licensed under the MIT License.
4
4
  # ------------------------------------
5
- import abc
6
5
  import os
7
- import sys
8
- from typing import cast, Any, Dict, Optional
9
- import warnings
6
+ import json
7
+ from typing import Any, Optional
10
8
 
9
+ import msal
11
10
  from azure.core.credentials import AccessToken, TokenRequestOptions, AccessTokenInfo
12
11
  from azure.core.exceptions import ClientAuthenticationError
12
+
13
+ from .._auth_record import AuthenticationRecord
13
14
  from .._exceptions import CredentialUnavailableError
14
- from .._constants import AzureAuthorityHosts, AZURE_VSCODE_CLIENT_ID, EnvironmentVariables
15
- from .._internal import normalize_authority, validate_tenant_id, within_dac
16
- from .._internal.aad_client import AadClient, AadClientBase
17
- from .._internal.get_token_mixin import GetTokenMixin
15
+ from .._constants import AZURE_VSCODE_CLIENT_ID
16
+ from .._internal import within_dac
17
+
18
18
  from .._internal.decorators import log_get_token
19
+ from .._internal.utils import get_broker_credential, validate_tenant_id
19
20
 
20
- if sys.platform.startswith("win"):
21
- from .._internal.win_vscode_adapter import get_refresh_token, get_user_settings
22
- elif sys.platform.startswith("darwin"):
23
- from .._internal.macos_vscode_adapter import get_refresh_token, get_user_settings
24
- else:
25
- from .._internal.linux_vscode_adapter import get_refresh_token, get_user_settings
21
+ MAX_AUTH_RECORD_SIZE = 10 * 1024 # 10KB - more than enough for a small auth record
22
+ VSCODE_AUTH_RECORD_PATHS = [
23
+ "~/.azure/ms-azuretools.vscode-azureresourcegroups/authRecord.json",
24
+ "~/.Azure/ms-azuretools.vscode-azureresourcegroups/authRecord.json",
25
+ ]
26
26
 
27
27
 
28
- class _VSCodeCredentialBase(abc.ABC):
29
- def __init__(self, **kwargs: Any) -> None:
30
- warnings.warn(
31
- "This credential is deprecated because the Azure Account extension for Visual Studio Code, which this "
32
- "credential relies on, has been deprecated. See the Azure Account extension deprecation notice here: "
33
- "https://github.com/microsoft/vscode-azure-account/issues/964. Consider using other developer credentials "
34
- "such as AzureCliCredential, AzureDeveloperCliCredential, or AzurePowerShellCredential.",
35
- DeprecationWarning,
36
- stacklevel=2,
37
- )
38
- super(_VSCodeCredentialBase, self).__init__()
39
-
40
- user_settings = get_user_settings()
41
- self._cloud = user_settings.get("azure.cloud", "AzureCloud")
42
- self._refresh_token = None
43
- self._unavailable_reason = ""
44
-
45
- self._client = kwargs.get("_client")
46
- if not self._client:
47
- self._initialize(user_settings, **kwargs)
48
- if not (self._client or self._unavailable_reason):
49
- self._unavailable_reason = "Initialization failed"
50
-
51
- @abc.abstractmethod
52
- def _get_client(self, **kwargs: Any) -> AadClientBase:
53
- pass
54
-
55
- def _get_refresh_token(self) -> str:
56
- if not self._refresh_token:
57
- self._refresh_token = get_refresh_token(self._cloud)
58
- if not self._refresh_token:
59
- message = (
60
- "Failed to get Azure user details from Visual Studio Code. "
61
- "Currently, the VisualStudioCodeCredential only works with the Azure "
62
- "Account extension version 0.9.11 and earlier. A long-term fix is in "
63
- "progress, see https://github.com/Azure/azure-sdk-for-python/issues/25713"
64
- )
65
- raise CredentialUnavailableError(message=message)
66
- return self._refresh_token
67
-
68
- def _initialize(self, vscode_user_settings: Dict, **kwargs: Any) -> None:
69
- """Build a client from kwargs merged with VS Code user settings.
28
+ def load_vscode_auth_record() -> Optional[AuthenticationRecord]:
29
+ """Load the authentication record corresponding to a known location.
70
30
 
71
- The first stable version of this credential defaulted to Public Cloud and the "organizations"
72
- tenant when it failed to read VS Code user settings. That behavior is preserved here.
31
+ This will load from ~/.azure/ms-azuretools.vscode-azureresourcegroups/authRecord.json
32
+ or ~/.Azure/ms-azuretools.vscode-azureresourcegroups/authRecord.json
73
33
 
74
- :param dict vscode_user_settings: VS Code user settings
75
- """
34
+ :return: The authentication record if it exists, otherwise None.
35
+ :rtype: Optional[AuthenticationRecord]
36
+ :raises: ValueError if the authentication record is not in the expected format
37
+ """
76
38
 
77
- # Precedence for authority:
78
- # 1) VisualStudioCodeCredential(authority=...)
79
- # 2) $AZURE_AUTHORITY_HOST
80
- # 3) authority matching VS Code's "azure.cloud" setting
81
- # 4) default: Public Cloud
82
- authority = kwargs.pop("authority", None) or os.environ.get(EnvironmentVariables.AZURE_AUTHORITY_HOST)
83
- if not authority:
84
- # the application didn't specify an authority, so we figure it out from VS Code settings
85
- if self._cloud == "AzureCloud":
86
- authority = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD
87
- elif self._cloud == "AzureChinaCloud":
88
- authority = AzureAuthorityHosts.AZURE_CHINA
89
- elif self._cloud == "AzureUSGovernment":
90
- authority = AzureAuthorityHosts.AZURE_GOVERNMENT
91
- else:
92
- # If the value is anything else ("AzureCustomCloud" is the only other known value),
93
- # we need the user to provide the authority because VS Code has no setting for it and
94
- # we can't guess confidently.
95
- self._unavailable_reason = (
96
- 'VS Code is configured to use a custom cloud. Set keyword argument "authority"'
97
- + ' with the Microsoft Entra endpoint for cloud "{}"'.format(self._cloud)
39
+ # Try each possible auth record path
40
+ for auth_record_path in VSCODE_AUTH_RECORD_PATHS:
41
+ expanded_path = os.path.expanduser(auth_record_path)
42
+ if os.path.exists(expanded_path):
43
+ file_size = os.path.getsize(expanded_path)
44
+ if file_size > MAX_AUTH_RECORD_SIZE:
45
+ error_message = (
46
+ "VS Code auth record file is unexpectedly large. "
47
+ "Please check the file for corruption or unexpected content."
98
48
  )
99
- return
100
-
101
- # Precedence for tenant ID:
102
- # 1) VisualStudioCodeCredential(tenant_id=...)
103
- # 2) "azure.tenant" in VS Code user settings
104
- # 3) default: organizations
105
- tenant_id = kwargs.pop("tenant_id", None) or vscode_user_settings.get("azure.tenant", "organizations")
106
- validate_tenant_id(tenant_id)
107
- if tenant_id.lower() == "adfs":
108
- self._unavailable_reason = "VisualStudioCodeCredential authentication unavailable. ADFS is not supported."
109
- return
110
-
111
- self._client = self._get_client(
112
- authority=normalize_authority(authority), client_id=AZURE_VSCODE_CLIENT_ID, tenant_id=tenant_id, **kwargs
113
- )
49
+ raise ValueError(error_message)
50
+ with open(expanded_path, "r", encoding="utf-8") as f:
51
+ deserialized = json.load(f)
52
+
53
+ # Validate the authentication record for security and structural integrity
54
+ _validate_auth_record_json(deserialized)
55
+
56
+ # Deserialize the authentication record
57
+ auth_record = AuthenticationRecord(
58
+ authority=deserialized["authority"],
59
+ client_id=deserialized["clientId"],
60
+ home_account_id=deserialized["homeAccountId"],
61
+ tenant_id=deserialized["tenantId"],
62
+ username=deserialized["username"],
63
+ )
64
+
65
+ return auth_record
114
66
 
67
+ # No auth record found in any of the expected locations
68
+ return None
115
69
 
116
- class VisualStudioCodeCredential(_VSCodeCredentialBase, GetTokenMixin):
117
- """Authenticates as the Azure user signed in to Visual Studio Code via the 'Azure Account' extension.
118
70
 
119
- **Deprecated**: This credential is deprecated because the Azure Account extension for Visual Studio Code, which
120
- this credential relies on, has been deprecated. See the Azure Account extension deprecation notice here:
121
- https://github.com/microsoft/vscode-azure-account/issues/964. Consider using other developer credentials such as
122
- AzureCliCredential, AzureDeveloperCliCredential, or AzurePowerShellCredential.
71
+ def _validate_auth_record_json(data: dict) -> None:
72
+ """Validate the authentication record.
123
73
 
124
- :keyword str authority: Authority of a Microsoft Entra endpoint, for example "login.microsoftonline.com".
125
- This argument is required for a custom cloud and usually unnecessary otherwise. Defaults to the authority
126
- matching the "Azure: Cloud" setting in VS Code's user settings or, when that setting has no value, the
127
- authority for Azure Public Cloud.
128
- :keyword str tenant_id: ID of the tenant the credential should authenticate in. Defaults to the "Azure: Tenant"
129
- setting in VS Code's user settings or, when that setting has no value, the "organizations" tenant, which
130
- supports only Microsoft Entra work or school accounts.
74
+ :param dict data: The authentication record data to validate.
75
+ :raises ValueError: If the authentication record fails validation checks.
76
+ """
77
+ errors = []
78
+
79
+ # Schema Validation - Required Fields
80
+ try:
81
+ tenant_id = data["tenantId"]
82
+ if not tenant_id or not isinstance(tenant_id, str):
83
+ errors.append("tenantId must be a non-empty string")
84
+ else:
85
+ try:
86
+ validate_tenant_id(tenant_id)
87
+ except ValueError as e:
88
+ errors.append(f"tenantId validation failed: {e}")
89
+ except KeyError:
90
+ errors.append("tenantId field is missing")
91
+
92
+ try:
93
+ client_id = data["clientId"]
94
+ if not client_id or not isinstance(client_id, str):
95
+ errors.append("clientId must be a non-empty string")
96
+ elif client_id != AZURE_VSCODE_CLIENT_ID:
97
+ errors.append(
98
+ f"clientId must match expected VS Code Azure Resources extension client ID: {AZURE_VSCODE_CLIENT_ID}"
99
+ )
100
+ except KeyError:
101
+ errors.append("clientId field is missing")
102
+
103
+ try:
104
+ username = data["username"]
105
+ if not username or not isinstance(username, str):
106
+ errors.append("username must be a non-empty string")
107
+ except KeyError:
108
+ errors.append("username field is missing")
109
+
110
+ try:
111
+ home_account_id = data["homeAccountId"]
112
+ if not home_account_id or not isinstance(home_account_id, str):
113
+ errors.append("homeAccountId must be a non-empty string")
114
+ except KeyError:
115
+ errors.append("homeAccountId field is missing")
116
+
117
+ try:
118
+ authority = data["authority"]
119
+ if not authority or not isinstance(authority, str):
120
+ errors.append("authority must be a non-empty string")
121
+ except KeyError:
122
+ errors.append("authority field is missing")
123
+
124
+ if errors:
125
+ error_message = "Authentication record validation failed: " + "; ".join(errors)
126
+ raise ValueError(error_message)
127
+
128
+
129
+ class VisualStudioCodeCredential:
130
+ """Authenticates as the Azure user signed in to Visual Studio Code via the 'Azure Resources' extension.
131
+
132
+ This currently only works in Windows/WSL environments and requires the 'azure-identity-broker'
133
+ package to be installed.
134
+
135
+ :keyword str tenant_id: A Microsoft Entra tenant ID. Defaults to the tenant specified in the authentication
136
+ record file used by the Azure Resources extension.
131
137
  :keyword List[str] additionally_allowed_tenants: Specifies tenants in addition to the specified "tenant_id"
132
138
  for which the credential may acquire tokens. Add the wildcard value "*" to allow the credential to
133
139
  acquire tokens for any tenant the application can access.
134
140
  """
135
141
 
142
+ def __init__(self, **kwargs: Any) -> None:
143
+
144
+ self._broker_credential = None
145
+ self._unavailable_message = (
146
+ "VisualStudioCodeCredential requires the 'azure-identity-broker' package to be installed. "
147
+ "You must also ensure you have the Azure Resources extension installed and have "
148
+ "signed in to Azure via Visual Studio Code."
149
+ )
150
+
151
+ broker_credential_class = get_broker_credential()
152
+ if broker_credential_class:
153
+ try:
154
+ # Load the authentication record from the VS Code extension
155
+ authentication_record = load_vscode_auth_record()
156
+ if not authentication_record:
157
+ self._unavailable_message = (
158
+ "VisualStudioCodeCredential requires the user to be signed in to Azure via Visual Studio Code. "
159
+ "Please ensure you have the Azure Resources extension installed and have signed in."
160
+ )
161
+ return
162
+ self._broker_credential = broker_credential_class(
163
+ client_id=AZURE_VSCODE_CLIENT_ID,
164
+ authentication_record=authentication_record,
165
+ parent_window_handle=msal.PublicClientApplication.CONSOLE_WINDOW_HANDLE,
166
+ use_default_broker_account=True,
167
+ disable_interactive_fallback=True,
168
+ **kwargs,
169
+ )
170
+ except ValueError as ex:
171
+ self._unavailable_message = (
172
+ "Failed to load authentication record from Visual Studio Code: "
173
+ f"{ex}. Please ensure you have the Azure Resources extension installed and signed in."
174
+ )
175
+
136
176
  def __enter__(self) -> "VisualStudioCodeCredential":
137
- if self._client:
138
- self._client.__enter__()
177
+ if self._broker_credential:
178
+ self._broker_credential.__enter__()
139
179
  return self
140
180
 
141
181
  def __exit__(self, *args: Any) -> None:
142
- if self._client:
143
- self._client.__exit__(*args)
182
+ if self._broker_credential:
183
+ self._broker_credential.__exit__(*args)
144
184
 
145
185
  def close(self) -> None:
146
186
  """Close the credential's transport session."""
147
- self.__exit__()
187
+ if self._broker_credential:
188
+ self._broker_credential.close()
148
189
 
149
190
  @log_get_token
150
191
  def get_token(
@@ -166,20 +207,15 @@ class VisualStudioCodeCredential(_VSCodeCredentialBase, GetTokenMixin):
166
207
  :raises ~azure.identity.CredentialUnavailableError: the credential cannot retrieve user details from Visual
167
208
  Studio Code
168
209
  """
169
- if self._unavailable_reason:
170
- error_message = (
171
- self._unavailable_reason + "\n"
172
- "Visit https://aka.ms/azsdk/python/identity/vscodecredential/troubleshoot"
173
- " to troubleshoot this issue."
174
- )
175
- raise CredentialUnavailableError(message=error_message)
210
+ if not self._broker_credential:
211
+ raise CredentialUnavailableError(message=self._unavailable_message)
176
212
  if within_dac.get():
177
213
  try:
178
- token = super().get_token(*scopes, claims=claims, tenant_id=tenant_id, **kwargs)
214
+ token = self._broker_credential.get_token(*scopes, claims=claims, tenant_id=tenant_id, **kwargs)
179
215
  return token
180
216
  except ClientAuthenticationError as ex:
181
217
  raise CredentialUnavailableError(message=ex.message) from ex
182
- return super().get_token(*scopes, claims=claims, tenant_id=tenant_id, **kwargs)
218
+ return self._broker_credential.get_token(*scopes, claims=claims, tenant_id=tenant_id, **kwargs)
183
219
 
184
220
  def get_token_info(self, *scopes: str, options: Optional[TokenRequestOptions] = None) -> AccessTokenInfo:
185
221
  """Request an access token for `scopes` as the user currently signed in to Visual Studio Code.
@@ -197,29 +233,12 @@ class VisualStudioCodeCredential(_VSCodeCredentialBase, GetTokenMixin):
197
233
  :raises ~azure.identity.CredentialUnavailableError: the credential cannot retrieve user details from Visual
198
234
  Studio Code.
199
235
  """
200
- if self._unavailable_reason:
201
- error_message = (
202
- self._unavailable_reason + "\n"
203
- "Visit https://aka.ms/azsdk/python/identity/vscodecredential/troubleshoot"
204
- " to troubleshoot this issue."
205
- )
206
- raise CredentialUnavailableError(message=error_message)
236
+ if not self._broker_credential:
237
+ raise CredentialUnavailableError(message=self._unavailable_message)
207
238
  if within_dac.get():
208
239
  try:
209
- token = super().get_token_info(*scopes, options=options)
240
+ token = self._broker_credential.get_token_info(*scopes, options=options)
210
241
  return token
211
242
  except ClientAuthenticationError as ex:
212
243
  raise CredentialUnavailableError(message=ex.message) from ex
213
- return super().get_token_info(*scopes, options=options)
214
-
215
- def _acquire_token_silently(self, *scopes: str, **kwargs: Any) -> Optional[AccessTokenInfo]:
216
- self._client = cast(AadClient, self._client)
217
- return self._client.get_cached_access_token(scopes, **kwargs)
218
-
219
- def _request_token(self, *scopes: str, **kwargs: Any) -> AccessTokenInfo:
220
- refresh_token = self._get_refresh_token()
221
- self._client = cast(AadClient, self._client)
222
- return self._client.obtain_token_by_refresh_token(scopes, refresh_token, **kwargs)
223
-
224
- def _get_client(self, **kwargs: Any) -> AadClient:
225
- return AadClient(**kwargs)
244
+ return self._broker_credential.get_token_info(*scopes, options=options)
@@ -11,6 +11,13 @@ from .client_assertion import ClientAssertionCredential
11
11
  from .._constants import EnvironmentVariables
12
12
 
13
13
 
14
+ WORKLOAD_CONFIG_ERROR = (
15
+ "WorkloadIdentityCredential authentication unavailable. The workload options are not fully "
16
+ "configured. See the troubleshooting guide for more information: "
17
+ "https://aka.ms/azsdk/python/identity/workloadidentitycredential/troubleshoot"
18
+ )
19
+
20
+
14
21
  class TokenFileMixin:
15
22
 
16
23
  _token_file_path: str
@@ -72,21 +79,25 @@ class WorkloadIdentityCredential(ClientAssertionCredential, TokenFileMixin):
72
79
  tenant_id = tenant_id or os.environ.get(EnvironmentVariables.AZURE_TENANT_ID)
73
80
  client_id = client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID)
74
81
  token_file_path = token_file_path or os.environ.get(EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE)
82
+
83
+ missing_args = []
75
84
  if not tenant_id:
76
- raise ValueError(
77
- "'tenant_id' is required. Please pass it in or set the "
78
- f"{EnvironmentVariables.AZURE_TENANT_ID} environment variable"
79
- )
85
+ missing_args.append("'tenant_id'")
80
86
  if not client_id:
81
- raise ValueError(
82
- "'client_id' is required. Please pass it in or set the "
83
- f"{EnvironmentVariables.AZURE_CLIENT_ID} environment variable"
84
- )
87
+ missing_args.append("'client_id'")
85
88
  if not token_file_path:
86
- raise ValueError(
87
- "'token_file_path' is required. Please pass it in or set the "
88
- f"{EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE} environment variable"
89
- )
89
+ missing_args.append("'token_file_path'")
90
+
91
+ if missing_args:
92
+ missing_args_str = ", ".join(missing_args)
93
+ error_message = f"{WORKLOAD_CONFIG_ERROR}. Missing required arguments: {missing_args_str}."
94
+ raise ValueError(error_message)
95
+
96
+ # Type assertions since we've validated these are not None
97
+ assert tenant_id is not None
98
+ assert client_id is not None
99
+ assert token_file_path is not None
100
+
90
101
  self._token_file_path = token_file_path
91
102
  super(WorkloadIdentityCredential, self).__init__(
92
103
  tenant_id=tenant_id,
@@ -11,6 +11,7 @@ from .interactive import InteractiveCredential
11
11
  from .utils import (
12
12
  get_default_authority,
13
13
  normalize_authority,
14
+ process_credential_exclusions,
14
15
  resolve_tenant,
15
16
  validate_scope,
16
17
  validate_subscription,
@@ -48,6 +49,7 @@ __all__ = [
48
49
  "get_default_authority",
49
50
  "InteractiveCredential",
50
51
  "normalize_authority",
52
+ "process_credential_exclusions",
51
53
  "resolve_tenant",
52
54
  "validate_scope",
53
55
  "validate_subscription",
@@ -63,7 +63,8 @@ def _get_policies(config, _per_retry_policies=None, **kwargs):
63
63
  def build_pipeline(transport=None, policies=None, **kwargs):
64
64
  if not policies:
65
65
  config = _get_config(**kwargs)
66
- config.retry_policy = RetryPolicy(**kwargs)
66
+ retry_policy_class = kwargs.pop("retry_policy_class", None)
67
+ config.retry_policy = retry_policy_class(**kwargs) if retry_policy_class else RetryPolicy(**kwargs)
67
68
  policies = _get_policies(config, **kwargs)
68
69
  if not transport:
69
70
  from azure.core.pipeline.transport import ( # pylint: disable=non-abstract-transport-import, no-name-in-module
@@ -82,7 +83,8 @@ def build_async_pipeline(transport=None, policies=None, **kwargs):
82
83
  from azure.core.pipeline.policies import AsyncRetryPolicy
83
84
 
84
85
  config = _get_config(**kwargs)
85
- config.retry_policy = AsyncRetryPolicy(**kwargs)
86
+ retry_policy_class = kwargs.pop("retry_policy_class", None)
87
+ config.retry_policy = retry_policy_class(**kwargs) if retry_policy_class else AsyncRetryPolicy(**kwargs)
86
88
  policies = _get_policies(config, **kwargs)
87
89
  if not transport:
88
90
  from azure.core.pipeline.transport import ( # pylint: disable=non-abstract-transport-import, no-name-in-module
@@ -3,6 +3,7 @@
3
3
  # Licensed under the MIT License.
4
4
  # ------------------------------------
5
5
  import os
6
+ import platform
6
7
  import logging
7
8
  from contextvars import ContextVar
8
9
  from string import ascii_letters, digits
@@ -133,3 +134,87 @@ def resolve_tenant(
133
134
  'when creating the credential, or add "*" to additionally_allowed_tenants to allow '
134
135
  "acquiring tokens for any tenant.".format(tenant_id)
135
136
  )
137
+
138
+
139
+ def process_credential_exclusions(credential_config: dict, exclude_flags: dict, user_excludes: dict) -> dict:
140
+ """Process credential exclusions based on environment variable and user overrides.
141
+
142
+ This method handles the AZURE_TOKEN_CREDENTIALS environment variable to determine
143
+ which credentials should be excluded from the credential chain, and then applies
144
+ any user-provided exclude overrides which take precedence over environment settings.
145
+
146
+ :param credential_config: Configuration mapping for all available credentials, containing
147
+ exclude parameter names, environment names, and default exclude settings
148
+ :type credential_config: dict
149
+ :param exclude_flags: Dictionary of exclude flags for each credential (will be modified)
150
+ :type exclude_flags: dict
151
+ :param user_excludes: User-provided exclude overrides from constructor kwargs
152
+ :type user_excludes: dict
153
+
154
+ :return: Dictionary of final exclude flags for each credential
155
+ :rtype: dict
156
+
157
+ :raises ValueError: If token_credentials_env contains an invalid credential name
158
+ """
159
+ # Handle AZURE_TOKEN_CREDENTIALS environment variable
160
+ token_credentials_env = os.environ.get(EnvironmentVariables.AZURE_TOKEN_CREDENTIALS, "").strip().lower()
161
+
162
+ if token_credentials_env == "dev":
163
+ # In dev mode, use only developer credentials
164
+ dev_credentials = {"visual_studio_code", "cli", "developer_cli", "powershell", "shared_token_cache"}
165
+ for cred_key in credential_config:
166
+ exclude_flags[cred_key] = cred_key not in dev_credentials
167
+ elif token_credentials_env == "prod":
168
+ # In prod mode, use only production credentials
169
+ prod_credentials = {"environment", "workload_identity", "managed_identity"}
170
+ for cred_key in credential_config:
171
+ exclude_flags[cred_key] = cred_key not in prod_credentials
172
+ elif token_credentials_env:
173
+ # If a specific credential is specified, exclude all others except the specified one
174
+ valid_credentials = {config["env_name"] for config in credential_config.values() if "env_name" in config}
175
+
176
+ if token_credentials_env not in valid_credentials:
177
+ valid_values = ["dev", "prod"] + sorted(valid_credentials)
178
+ raise ValueError(
179
+ f"Invalid value for {EnvironmentVariables.AZURE_TOKEN_CREDENTIALS}: {token_credentials_env}. "
180
+ f"Valid values are: {', '.join(valid_values)}."
181
+ )
182
+
183
+ # Find which credential was selected and exclude all others
184
+ selected_cred_key = None
185
+ for cred_key, config in credential_config.items():
186
+ if config.get("env_name") == token_credentials_env:
187
+ selected_cred_key = cred_key
188
+ break
189
+
190
+ for cred_key in credential_config:
191
+ exclude_flags[cred_key] = cred_key != selected_cred_key
192
+
193
+ # Apply user-provided exclude flags (these override environment variable settings)
194
+ for cred_key, user_value in user_excludes.items():
195
+ if user_value is not None:
196
+ exclude_flags[cred_key] = user_value
197
+
198
+ return exclude_flags
199
+
200
+
201
+ def get_broker_credential() -> Optional[type]:
202
+ """Return the InteractiveBrowserBrokerCredential class if available, otherwise None.
203
+
204
+ :return: InteractiveBrowserBrokerCredential class or None
205
+ :rtype: Optional[type]
206
+ """
207
+ try:
208
+ from azure.identity.broker import InteractiveBrowserBrokerCredential
209
+
210
+ return InteractiveBrowserBrokerCredential
211
+ except ImportError:
212
+ return None
213
+
214
+
215
+ def is_wsl() -> bool:
216
+ # This is how MSAL checks for WSL.
217
+ uname = platform.uname()
218
+ platform_name = getattr(uname, "system", uname[0]).lower()
219
+ release = getattr(uname, "release", uname[2]).lower()
220
+ return platform_name == "linux" and "microsoft" in release
@@ -2,4 +2,4 @@
2
2
  # Copyright (c) Microsoft Corporation.
3
3
  # Licensed under the MIT License.
4
4
  # ------------------------------------
5
- VERSION = "1.23.1"
5
+ VERSION = "1.24.0"
@@ -133,7 +133,7 @@ class AuthorizationCodeCredential(AsyncContextManager, GetTokenMixin):
133
133
  return token
134
134
 
135
135
  token = cast(AccessTokenInfo, None)
136
- for refresh_token in self._client.get_cached_refresh_tokens(scopes):
136
+ for refresh_token in self._client.get_cached_refresh_tokens(scopes, **kwargs):
137
137
  if "secret" in refresh_token:
138
138
  token = await self._client.obtain_token_by_refresh_token(scopes, refresh_token["secret"], **kwargs)
139
139
  if token: