From cef385df46077de630a47a5867dada92d18976dc Mon Sep 17 00:00:00 2001 From: Paul Van Eck Date: Wed, 27 Aug 2025 02:18:55 +0000 Subject: [PATCH 1/2] [Identity] Update tenant resolution logic Our tenant resolution logic shouldn't error out requests for certain dev credentials (e.g. `DeviceCodeCredential and `InteractiveBrowserCredential`) if a tenant is passed into `get_token` or `get_token_info`. This happens if the no tenant was configured on the credential and the default "organizations" tenant is used. Signed-off-by: Paul Van Eck --- .../azure/identity/_internal/utils.py | 5 ++ .../tests/test_device_code_credential.py | 53 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/sdk/identity/azure-identity/azure/identity/_internal/utils.py b/sdk/identity/azure-identity/azure/identity/_internal/utils.py index 1e0c1e64e7f9..1f3a41cbf701 100644 --- a/sdk/identity/azure-identity/azure/identity/_internal/utils.py +++ b/sdk/identity/azure-identity/azure/identity/_internal/utils.py @@ -128,6 +128,11 @@ def resolve_tenant( tenant_id, ) return tenant_id + # Some dev credentials commonly default to the "organizations" special tenant which can authenticate users against + # multiple tenants that the user belongs to. If an allowed tenant list was not provided and the credential's + # tenant is set to 'organizations', allow the request with the specified tenant ID. + if not additionally_allowed_tenants and default_tenant == "organizations": + return tenant_id raise ClientAuthenticationError( message="The current credential is not configured to acquire tokens for tenant {}. " "To enable acquiring tokens for this tenant add it to the additionally_allowed_tenants " diff --git a/sdk/identity/azure-identity/tests/test_device_code_credential.py b/sdk/identity/azure-identity/tests/test_device_code_credential.py index c1bebe13de29..289e293e20ce 100644 --- a/sdk/identity/azure-identity/tests/test_device_code_credential.py +++ b/sdk/identity/azure-identity/tests/test_device_code_credential.py @@ -236,6 +236,7 @@ def test_device_code_credential(get_token_method): @pytest.mark.parametrize("get_token_method", GET_TOKEN_METHODS) def test_tenant_id(get_token_method): client_id = "client-id" + tenant_id = "tenant-id" expected_token = "access-token" user_code = "user-code" verification_uri = "verification-uri" @@ -271,12 +272,64 @@ def test_tenant_id(get_token_method): callback = Mock() credential = DeviceCodeCredential( client_id=client_id, + tenant_id=tenant_id, prompt_callback=callback, transport=transport, disable_instance_discovery=True, additionally_allowed_tenants=["*"], ) + kwargs = {"tenant_id": "tenant-id-2"} + if get_token_method == "get_token_info": + kwargs = {"options": kwargs} + token = getattr(credential, get_token_method)("scope", **kwargs) + assert token.token == expected_token + + +@pytest.mark.parametrize("get_token_method", GET_TOKEN_METHODS) +def test_default_tenant_id(get_token_method): + """If no tenant_id is provided, token request tenants should be allowed""" + client_id = "client-id" + expected_token = "access-token" + user_code = "user-code" + verification_uri = "verification-uri" + expires_in = 42 + + transport = validating_transport( + requests=[Request()] * 3, # not validating requests because they're formed by MSAL + responses=[ + # expected requests: discover tenant, start device code flow, poll for completion + get_discovery_response(), + mock_response( + json_payload={ + "device_code": "_", + "user_code": user_code, + "verification_uri": verification_uri, + "expires_in": expires_in, + } + ), + mock_response( + json_payload=dict( + build_aad_response( + access_token=expected_token, + expires_in=expires_in, + refresh_token="_", + id_token=build_id_token(aud=client_id), + ), + scope="scope", + ), + ), + ], + ) + + callback = Mock() + credential = DeviceCodeCredential( + client_id=client_id, + prompt_callback=callback, + transport=transport, + disable_instance_discovery=True, + ) + kwargs = {"tenant_id": "tenant_id"} if get_token_method == "get_token_info": kwargs = {"options": kwargs} From c258d1896f2d84c471854778069d6f5e55132273 Mon Sep 17 00:00:00 2001 From: Paul Van Eck Date: Wed, 27 Aug 2025 18:59:08 +0000 Subject: [PATCH 2/2] Update changelog Signed-off-by: Paul Van Eck --- sdk/identity/azure-identity/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/identity/azure-identity/CHANGELOG.md b/sdk/identity/azure-identity/CHANGELOG.md index 0c5ea8701ec1..e8685e8c3549 100644 --- a/sdk/identity/azure-identity/CHANGELOG.md +++ b/sdk/identity/azure-identity/CHANGELOG.md @@ -12,6 +12,7 @@ ### Bugs Fixed - Fixed an issue where `AzureDeveloperCliCredential` would time out during token requests when `azd` prompts for user interaction. This issue commonly occurred in environments where the `AZD_DEBUG` environment variable was set, causing the Azure Developer CLI to display additional prompts that interfered with automated token acquisition. ([#42535](https://github.com/Azure/azure-sdk-for-python/pull/42535)) +- Fixed an issue where credentials configured with a default tenant ID of "organizations" (such as `InteractiveBrowserCredential` and `DeviceCodeCredential`) would fail authentication when a specific tenant ID was provided in `get_token` or `get_token_info` method calls. ([#42721](https://github.com/Azure/azure-sdk-for-python/pull/42721)) ### Other Changes