Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Multi-Tenant support to incorporate Arch Board Feedback #20940

Merged
merged 11 commits into from
Oct 1, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions sdk/identity/azure-identity/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

### Features Added

xiangyan99 marked this conversation as resolved.
Show resolved Hide resolved
- `allow_multitenant_authentication` argument is deprecated and the setting is always on. To
xiangyan99 marked this conversation as resolved.
Show resolved Hide resolved
disable it, set environment variable
AZURE_IDENTITY_DISABLE_MULTITENANTAUTH to "True".
- `azure.identity.RegionalAuthority` is removed.
xiangyan99 marked this conversation as resolved.
Show resolved Hide resolved

### Breaking Changes

### Bugs Fixed
Expand Down
2 changes: 0 additions & 2 deletions sdk/identity/azure-identity/azure/identity/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"""Credentials for Azure SDK clients."""

from ._auth_record import AuthenticationRecord
from ._enums import RegionalAuthority
from ._exceptions import AuthenticationRequiredError, CredentialUnavailableError
from ._constants import AzureAuthorityHosts, KnownAuthorities
from ._credentials import (
Expand Down Expand Up @@ -47,7 +46,6 @@
"InteractiveBrowserCredential",
"KnownAuthorities",
"OnBehalfOfCredential",
"RegionalAuthority",
"ManagedIdentityCredential",
"SharedTokenCacheCredential",
"TokenCachePersistenceOptions",
Expand Down
2 changes: 1 addition & 1 deletion sdk/identity/azure-identity/azure/identity/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class EnvironmentVariables:
MSI_SECRET = "MSI_SECRET"

AZURE_AUTHORITY_HOST = "AZURE_AUTHORITY_HOST"
AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION = "AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION"
AZURE_IDENTITY_DISABLE_MULTITENANTAUTH = "AZURE_IDENTITY_DISABLE_MULTITENANTAUTH"
AZURE_REGIONAL_AUTHORITY_NAME = "AZURE_REGIONAL_AUTHORITY_NAME"

AZURE_FEDERATED_TOKEN_FILE = "AZURE_FEDERATED_TOKEN_FILE"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,6 @@ class AzureApplicationCredential(ChainedTokenCredential):
<https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview>`_ for an overview of
managed identities.

:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
the application or user is registered in. When False, which is the default, the credential will acquire tokens
only from the tenant specified by **AZURE_TENANT_ID**. This argument doesn't apply to managed identity
authentication.
:keyword str authority: Authority of an Azure Active Directory endpoint, for example "login.microsoftonline.com",
the authority for Azure Public Cloud, which is the default when no value is given for this keyword argument or
environment variable AZURE_AUTHORITY_HOST. :class:`~azure.identity.AzureAuthorityHosts` defines authorities for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ class AuthorizationCodeCredential(GetTokenMixin):
the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts`
defines authorities for other clouds.
:keyword str client_secret: One of the application's client secrets. Required only for web apps and web APIs.
:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
the user is registered in. When False, which is the default, the credential will acquire tokens only from the
user's home tenant or the tenant specified by **tenant_id**.
"""

def __init__(self, tenant_id, client_id, authorization_code, redirect_uri, **kwargs):
Expand Down Expand Up @@ -67,8 +64,7 @@ def get_token(self, *scopes, **kwargs):
redeeming the authorization code.

:param str scopes: desired scopes for the access token. This method requires at least one scope.
:keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication**
is False, specifying a tenant with this argument may raise an exception.
:keyword str tenant_id: optional tenant to include in the token request.

:rtype: :class:`azure.core.credentials.AccessToken`
:raises ~azure.core.exceptions.ClientAuthenticationError: authentication failed. The error's ``message``
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .. import CredentialUnavailableError
from .._internal import _scopes_to_resource, resolve_tenant
from .._internal.decorators import log_get_token
from .._constants import EnvironmentVariables

if TYPE_CHECKING:
# pylint:disable=ungrouped-imports
Expand All @@ -35,14 +36,11 @@ class AzureCliCredential(object):
"""Authenticates by requesting a token from the Azure CLI.

This requires previously logging in to Azure via "az login", and will use the CLI's currently logged in identity.

:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
the identity logged in to the Azure CLI is registered in. When False, which is the default, the credential will
acquire tokens only from the tenant of the Azure CLI's active subscription.
"""

def __init__(self, **kwargs):
self._allow_multitenant = kwargs.get("allow_multitenant_authentication", False)
disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False)
xiangyan99 marked this conversation as resolved.
Show resolved Hide resolved
self._allow_multitenant = not disable_multitenant

def __enter__(self):
return self
Expand All @@ -63,8 +61,7 @@ def get_token(self, *scopes, **kwargs):
also handle token caching because this credential doesn't cache the tokens it acquires.

:param str scopes: desired scope for the access token. This credential allows only one scope per request.
:keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication**
is False, specifying a tenant with this argument may raise an exception.
:keyword str tenant_id: optional tenant to include in the token request.

:rtype: :class:`azure.core.credentials.AccessToken`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# ------------------------------------
import base64
import logging
import os
import platform
import subprocess
import sys
Expand All @@ -18,6 +19,7 @@
from .. import CredentialUnavailableError
from .._internal import _scopes_to_resource, resolve_tenant
from .._internal.decorators import log_get_token
from .._constants import EnvironmentVariables

if TYPE_CHECKING:
# pylint:disable=ungrouped-imports
Expand Down Expand Up @@ -51,15 +53,12 @@ class AzurePowerShellCredential(object):
"""Authenticates by requesting a token from Azure PowerShell.

This requires previously logging in to Azure via "Connect-AzAccount", and will use the currently logged in identity.

:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
the identity logged in to Azure PowerShell is registered in. When False, which is the default, the credential
will acquire tokens only from the tenant of Azure PowerShell's active subscription.
"""

def __init__(self, **kwargs):
# type: (**Any) -> None
self._allow_multitenant = kwargs.get("allow_multitenant_authentication", False)
disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False)
self._allow_multitenant = not disable_multitenant

def __enter__(self):
return self
Expand All @@ -80,8 +79,7 @@ def get_token(self, *scopes, **kwargs):
also handle token caching because this credential doesn't cache the tokens it acquires.

:param str scopes: desired scope for the access token. This credential allows only one scope per request.
:keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication**
is False, specifying a tenant with this argument may raise an exception.
:keyword str tenant_id: optional tenant to include in the token request.

:rtype: :class:`azure.core.credentials.AccessToken`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ class InteractiveBrowserCredential(InteractiveCredential):
will cache tokens in memory.
:paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions
:keyword int timeout: seconds to wait for the user to complete authentication. Defaults to 300 (5 minutes).
:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
the user is registered in. When False, which is the default, the credential will acquire tokens only from the
user's home tenant or the tenant specified by **tenant_id**.
:raises ValueError: invalid **redirect_uri**
"""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,14 @@ class CertificateCredential(ClientCredentialBase):
:keyword password: The certificate's password. If a unicode string, it will be encoded as UTF-8. If the certificate
requires a different encoding, pass appropriately encoded bytes instead.
:paramtype password: str or bytes
:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
the application is registered in. When False, which is the default, the credential will acquire tokens only from
the tenant specified by **tenant_id**.
:keyword bool send_certificate_chain: if True, the credential will send the public certificate chain in the x5c
header of each token request's JWT. This is required for Subject Name/Issuer (SNI) authentication. Defaults to
False.
:keyword cache_persistence_options: configuration for persistent token caching. If unspecified, the credential
will cache tokens in memory.
:paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions
:keyword ~azure.identity.RegionalAuthority regional_authority: a :class:`~azure.identity.RegionalAuthority` to
which the credential will authenticate. This argument should be used only by applications deployed to Azure
VMs.
:keyword str regional_authority: to which the credential will authenticate.
This argument should be used only by applications deployed to Azure VMs.
"""

def __init__(self, tenant_id, client_id, certificate_path=None, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,11 @@ class ClientSecretCredential(ClientCredentialBase):
:keyword str authority: Authority of an Azure Active Directory endpoint, for example "login.microsoftonline.com",
the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts`
defines authorities for other clouds.
:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
the application is registered in. When False, which is the default, the credential will acquire tokens only from
the tenant specified by **tenant_id**.
:keyword cache_persistence_options: configuration for persistent token caching. If unspecified, the credential
will cache tokens in memory.
:paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions
:keyword ~azure.identity.RegionalAuthority regional_authority: a :class:`~azure.identity.RegionalAuthority` to
which the credential will authenticate. This argument should be used only by applications deployed to Azure
VMs.
:keyword str regional_authority: to which the credential will authenticate.
This argument should be used only by applications deployed to Azure VMs.
"""

def __init__(self, tenant_id, client_id, client_secret, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,6 @@ class DefaultAzureCredential(ChainedTokenCredential):

This default behavior is configurable with keyword arguments.

:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
the application is registered in. When False, which is the default, the credential will acquire tokens only from
its configured tenant. This argument doesn't apply to managed identity authentication.
:keyword str authority: Authority of an Azure Active Directory endpoint, for example 'login.microsoftonline.com',
the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts`
defines authorities for other clouds. Managed identities ignore this because they reside in a single cloud.
Expand Down Expand Up @@ -158,8 +155,7 @@ def get_token(self, *scopes, **kwargs):
This method is called automatically by Azure SDK clients.

:param str scopes: desired scopes for the access token. This method requires at least one scope.
:keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication**
is False, specifying a tenant with this argument may raise an exception.
:keyword str tenant_id: optional tenant to include in the token request.

:rtype: :class:`azure.core.credentials.AccessToken`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,6 @@ class DeviceCodeCredential(InteractiveCredential):
:keyword cache_persistence_options: configuration for persistent token caching. If unspecified, the credential
will cache tokens in memory.
:paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions
:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
the user is registered in. When False, which is the default, the credential will acquire tokens only from the
user's home tenant or the tenant specified by **tenant_id**.
"""

def __init__(self, client_id=DEVELOPER_SIGN_ON_CLIENT_ID, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ class EnvironmentCredential(object):
- **AZURE_TENANT_ID**: (optional) ID of the service principal's tenant. Also called its 'directory' ID.
If not provided, defaults to the 'organizations' tenant, which supports only Azure Active Directory work or
school accounts.

:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
the application or user is registered in. When False, which is the default, the credential will acquire tokens
only from the tenant specified by **AZURE_TENANT_ID**.
"""

def __init__(self, **kwargs):
Expand Down Expand Up @@ -123,8 +119,7 @@ def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument
This method is called automatically by Azure SDK clients.

:param str scopes: desired scopes for the access token. This method requires at least one scope.
:keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication**
is False, specifying a tenant with this argument may raise an exception.
:keyword str tenant_id: optional tenant to include in the token request.

:rtype: :class:`azure.core.credentials.AccessToken`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@ class OnBehalfOfCredential(MsalCredential, GetTokenMixin):
:param str user_assertion: the access token the credential will use as the user assertion when requesting
on-behalf-of tokens

:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
the application is registered in. When False, which is the default, the credential will acquire tokens only
from the tenant specified by **tenant_id**.
:keyword str authority: Authority of an Azure Active Directory endpoint, for example "login.microsoftonline.com",
the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts`
defines authorities for other clouds.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ class SharedTokenCacheCredential(object):
:keyword cache_persistence_options: configuration for persistent token caching. If not provided, the credential
will use the persistent cache shared by Microsoft development applications
:paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions
:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
the user is registered in. When False, which is the default, the credential will acquire tokens only from the
user's home tenant or, if a value was given for **authentication_record**, the tenant specified by the
:class:`AuthenticationRecord`.
"""

def __init__(self, username=None, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from .._internal.msal_client import MsalClient
from .._internal.shared_token_cache import NO_TOKEN
from .._persistent_cache import _load_persistent_cache, TokenCachePersistenceOptions
from .._constants import EnvironmentVariables

if TYPE_CHECKING:
# pylint:disable=unused-import,ungrouped-imports
Expand All @@ -35,7 +36,11 @@ def __init__(self, authentication_record, **kwargs):
# authenticate in the tenant that produced the record unless "tenant_id" specifies another
self._tenant_id = kwargs.pop("tenant_id", None) or self._auth_record.tenant_id
validate_tenant_id(self._tenant_id)
self._allow_multitenant = kwargs.pop("allow_multitenant_authentication", False)
if self._tenant_id == "adfs":
self._allow_multitenant = False
else:
disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False)
self._allow_multitenant = not disable_multitenant
self._cache = kwargs.pop("_cache", None)
self._client_applications = {} # type: Dict[str, PublicClientApplication]
self._client = MsalClient(**kwargs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ class UsernamePasswordCredential(InteractiveCredential):
:keyword cache_persistence_options: configuration for persistent token caching. If unspecified, the credential
will cache tokens in memory.
:paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions
:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
the user is registered in. When False, which is the default, the credential will acquire tokens only from the
user's home tenant or the tenant specified by **tenant_id**.
"""

def __init__(self, client_id, username, password, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,6 @@ class VisualStudioCodeCredential(_VSCodeCredentialBase, GetTokenMixin):
:keyword str tenant_id: ID of the tenant the credential should authenticate in. Defaults to the "Azure: Tenant"
setting in VS Code's user settings or, when that setting has no value, the "organizations" tenant, which
supports only Azure Active Directory work or school accounts.
:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
the user is registered in. When False, which is the default, the credential will acquire tokens only from the
user's home tenant or the tenant configured by **tenant_id** or VS Code's user settings.
"""

def __enter__(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,20 @@ def validate_tenant_id(tenant_id):
)


def resolve_tenant(default_tenant, allow_multitenant, tenant_id=None, **_):
# type: (str, bool, Optional[str], **Any) -> str
def resolve_tenant(default_tenant, allow_multitenant=True, tenant_id=None, **_):
# type: (str, Optional[bool], Optional[str], **Any) -> str
"""Returns the correct tenant for a token request given a credential's configuration"""
if (
tenant_id is None
or tenant_id == default_tenant
or os.environ.get(EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION)
):
return default_tenant

if not allow_multitenant:
disable_multitenant = os.environ.get(EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH, False)
xiangyan99 marked this conversation as resolved.
Show resolved Hide resolved
if disable_multitenant or not allow_multitenant:
raise ClientAuthenticationError(
'The specified tenant for this token request, "{}", does not match'.format(tenant_id)
+ ' the configured tenant, and "allow_multitenant_authentication" is False.'
+ ' the configured tenant, and "multitenant_authentication" is disabled.'
)
return tenant_id

Expand Down
Loading