diff --git a/sdk/identity/azure-identity/CHANGELOG.md b/sdk/identity/azure-identity/CHANGELOG.md index 7b2bef081a3c..4f304bc58673 100644 --- a/sdk/identity/azure-identity/CHANGELOG.md +++ b/sdk/identity/azure-identity/CHANGELOG.md @@ -1,6 +1,12 @@ # Release History ## 1.4.0b4 (Unreleased) +- When constructing `DefaultAzureCredential`, you can now configure a tenant ID + for `InteractiveBrowserCredential`. When none is specified, the credential + authenticates users in their home tenants. To specify a different tenant, use + the keyword argument `interactive_browser_tenant_id`, or set the environment + variable `AZURE_TENANT_ID`. + ([#11548](https://github.com/Azure/azure-sdk-for-python/issues/11548)) - The user authentication API added to `DeviceCodeCredential` and `InteractiveBrowserCredential` in 1.4.0b3 is available on `UsernamePasswordCredential` as well. diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/_credentials/default.py index 74a05de08904..c78dbc3503d0 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/default.py @@ -59,6 +59,9 @@ class DefaultAzureCredential(ChainedTokenCredential): **False**. :keyword bool exclude_interactive_browser_credential: Whether to exclude interactive browser authentication (see :class:`~azure.identity.InteractiveBrowserCredential`). Defaults to **True**. + :keyword str interactive_browser_tenant_id: Tenant ID to use when authenticating a user through + :class:`~azure.identity.InteractiveBrowserCredential`. Defaults to the value of environment variable + AZURE_TENANT_ID, if any. If unspecified, users will authenticate in their home tenants. :keyword str shared_cache_username: Preferred username for :class:`~azure.identity.SharedTokenCacheCredential`. Defaults to the value of environment variable AZURE_USERNAME, if any. :keyword str shared_cache_tenant_id: Preferred tenant for :class:`~azure.identity.SharedTokenCacheCredential`. @@ -69,6 +72,10 @@ def __init__(self, **kwargs): authority = kwargs.pop("authority", None) authority = normalize_authority(authority) if authority else get_default_authority() + interactive_browser_tenant_id = kwargs.pop( + "interactive_browser_tenant_id", os.environ.get(EnvironmentVariables.AZURE_TENANT_ID) + ) + shared_cache_username = kwargs.pop("shared_cache_username", os.environ.get(EnvironmentVariables.AZURE_USERNAME)) shared_cache_tenant_id = kwargs.pop( "shared_cache_tenant_id", os.environ.get(EnvironmentVariables.AZURE_TENANT_ID) @@ -101,7 +108,7 @@ def __init__(self, **kwargs): if not exclude_cli_credential: credentials.append(AzureCliCredential()) if not exclude_interactive_browser_credential: - credentials.append(InteractiveBrowserCredential()) + credentials.append(InteractiveBrowserCredential(tenant_id=interactive_browser_tenant_id)) super(DefaultAzureCredential, self).__init__(*credentials) diff --git a/sdk/identity/azure-identity/tests/test_default.py b/sdk/identity/azure-identity/tests/test_default.py index c21658b56da7..ac38d9e3d417 100644 --- a/sdk/identity/azure-identity/tests/test_default.py +++ b/sdk/identity/azure-identity/tests/test_default.py @@ -255,3 +255,30 @@ def get_credential_for_shared_cache_test(expected_refresh_token, expected_access # this credential uses a mock shared cache, so it works on all platforms with patch.object(SharedTokenCacheCredential, "supported"): return DefaultAzureCredential(_cache=cache, transport=transport, **options) + + +def test_interactive_browser_tenant_id(): + """the credential should allow configuring a tenant ID for InteractiveBrowserCredential by kwarg or environment""" + + tenant_id = "tenant-id" + + def validate_tenant_id(credential): + assert len(credential.call_args_list) == 1, "InteractiveBrowserCredential should be instantiated once" + _, kwargs = credential.call_args + assert kwargs == {'tenant_id': tenant_id} + + with patch(DefaultAzureCredential.__module__ + ".InteractiveBrowserCredential") as mock_credential: + DefaultAzureCredential(exclude_interactive_browser_credential=False, interactive_browser_tenant_id=tenant_id) + validate_tenant_id(mock_credential) + + # tenant id can also be specified in $AZURE_TENANT_ID + with patch.dict(os.environ, {EnvironmentVariables.AZURE_TENANT_ID: tenant_id}, clear=True): + with patch(DefaultAzureCredential.__module__ + ".InteractiveBrowserCredential") as mock_credential: + DefaultAzureCredential(exclude_interactive_browser_credential=False) + validate_tenant_id(mock_credential) + + # keyword argument should override environment variable + with patch.dict(os.environ, {EnvironmentVariables.AZURE_TENANT_ID: "not-" + tenant_id}, clear=True): + with patch(DefaultAzureCredential.__module__ + ".InteractiveBrowserCredential") as mock_credential: + DefaultAzureCredential(exclude_interactive_browser_credential=False, interactive_browser_tenant_id=tenant_id) + validate_tenant_id(mock_credential)