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

Credentials accept tenant_id argument to get_token #19602

Merged
merged 19 commits into from
Jul 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions sdk/identity/azure-identity/azure/identity/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ 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_REGIONAL_AUTHORITY_NAME = "AZURE_REGIONAL_AUTHORITY_NAME"
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ def get_token(self, *scopes, **kwargs):
)
return super(AppServiceCredential, self).get_token(*scopes, **kwargs)

def _acquire_token_silently(self, *scopes):
# type: (*str) -> Optional[AccessToken]
def _acquire_token_silently(self, *scopes, **kwargs):
# type: (*str, **Any) -> Optional[AccessToken]
return self._client.get_cached_token(*scopes)

def _request_token(self, *scopes, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ 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 @@ -10,25 +10,29 @@

if TYPE_CHECKING:
# pylint:disable=unused-import,ungrouped-imports
from typing import Any, Iterable, Optional
from typing import Any, Optional
from azure.core.credentials import AccessToken


class AuthorizationCodeCredential(GetTokenMixin):
"""Authenticates by redeeming an authorization code previously obtained from Azure Active Directory.

See https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-auth-code-flow for more information
See `Azure Active Directory documentation
<https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-auth-code-flow>`_ for more information
about the authentication flow.

:param str tenant_id: ID of the application's Azure Active Directory tenant. Also called its 'directory' ID.
:param str tenant_id: ID of the application's Azure Active Directory tenant. Also called its "directory" ID.
:param str client_id: the application's client ID
:param str authorization_code: the authorization code from the user's log-in
:param str redirect_uri: The application's redirect URI. Must match the URI used to request the authorization code.

: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 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 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 All @@ -51,16 +55,20 @@ 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.

:rtype: :class:`azure.core.credentials.AccessToken`
:raises ~azure.core.exceptions.ClientAuthenticationError: authentication failed. The error's ``message``
attribute gives a reason. Any error response from Azure Active Directory is available as the error's
``response`` attribute.
"""
return super(AuthorizationCodeCredential, self).get_token(*scopes)
# pylint:disable=useless-super-delegation
return super(AuthorizationCodeCredential, self).get_token(*scopes, **kwargs)

def _acquire_token_silently(self, *scopes):
# type: (*str) -> Optional[AccessToken]
return self._client.get_cached_access_token(scopes)
def _acquire_token_silently(self, *scopes, **kwargs):
# type: (*str, **Any) -> Optional[AccessToken]
return self._client.get_cached_access_token(scopes, **kwargs)

def _request_token(self, *scopes, **kwargs):
# type: (*str, **Any) -> AccessToken
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ def get_token(self, *scopes, **kwargs):
)
return super(AzureArcCredential, self).get_token(*scopes, **kwargs)

def _acquire_token_silently(self, *scopes):
# type: (*str) -> Optional[AccessToken]
def _acquire_token_silently(self, *scopes, **kwargs):
# type: (*str, **Any) -> Optional[AccessToken]
return self._client.get_cached_token(*scopes)

def _request_token(self, *scopes, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from azure.core.exceptions import ClientAuthenticationError

from .. import CredentialUnavailableError
from .._internal import _scopes_to_resource
from .._internal import _scopes_to_resource, resolve_tenant
from .._internal.decorators import log_get_token

if TYPE_CHECKING:
Expand All @@ -35,17 +35,27 @@ 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)

@log_get_token("AzureCliCredential")
def get_token(self, *scopes, **kwargs): # pylint:disable=no-self-use,unused-argument
def get_token(self, *scopes, **kwargs):
# type: (*str, **Any) -> AccessToken
"""Request an access token for `scopes`.

This method is called automatically by Azure SDK clients. Applications calling this method directly must
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.

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

:raises ~azure.identity.CredentialUnavailableError: the credential was unable to invoke the Azure CLI.
Expand All @@ -54,7 +64,11 @@ def get_token(self, *scopes, **kwargs): # pylint:disable=no-self-use,unused-arg
"""

resource = _scopes_to_resource(*scopes)
output = _run_command(COMMAND_LINE.format(resource))
command = COMMAND_LINE.format(resource)
tenant = resolve_tenant("", self._allow_multitenant, **kwargs)
if tenant:
command += " --tenant " + tenant
output = _run_command(command)

token = parse_token(output)
if not token:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from .azure_cli import get_safe_working_dir
from .. import CredentialUnavailableError
from .._internal import _scopes_to_resource
from .._internal import _scopes_to_resource, resolve_tenant
from .._internal.decorators import log_get_token

if TYPE_CHECKING:
Expand All @@ -41,7 +41,7 @@
exit
}}

$token = Get-AzAccessToken -ResourceUrl '{}'
$token = Get-AzAccessToken -ResourceUrl '{}'{}

Write-Output "`nazsdk%$($token.Token)%$($token.ExpiresOn.ToUnixTimeSeconds())`n"
"""
Expand All @@ -51,26 +51,37 @@ 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)

@log_get_token("AzurePowerShellCredential")
def get_token(self, *scopes, **kwargs): # pylint:disable=no-self-use,unused-argument
def get_token(self, *scopes, **kwargs):
# type: (*str, **Any) -> AccessToken
"""Request an access token for `scopes`.

This method is called automatically by Azure SDK clients. Applications calling this method directly must
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.

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

:raises ~azure.identity.CredentialUnavailableError: the credential was unable to invoke Azure PowerShell, or
no account is authenticated
:raises ~azure.core.exceptions.ClientAuthenticationError: the credential invoked Azure PowerShell but didn't
receive an access token
"""

command_line = get_command_line(scopes)
tenant_id = resolve_tenant("", self._allow_multitenant, **kwargs)
command_line = get_command_line(scopes, tenant_id)
output = run_command_line(command_line)
token = parse_token(output)
return token
Expand Down Expand Up @@ -128,10 +139,14 @@ def parse_token(output):
raise ClientAuthenticationError(message='Unexpected output from Get-AzAccessToken: "{}"'.format(output))


def get_command_line(scopes):
# type: (Tuple) -> List[str]
def get_command_line(scopes, tenant_id):
# type: (Tuple, str) -> List[str]
if tenant_id:
tenant_argument = " -TenantId " + tenant_id
else:
tenant_argument = ""
resource = _scopes_to_resource(*scopes)
script = SCRIPT.format(NO_AZ_ACCOUNT_MODULE, resource)
script = SCRIPT.format(NO_AZ_ACCOUNT_MODULE, resource, tenant_argument)
encoded_script = base64.b64encode(script.encode("utf-16-le")).decode()

command = "pwsh -NonInteractive -EncodedCommand " + encoded_script
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,18 @@ class InteractiveBrowserCredential(InteractiveCredential):
:func:`~get_token` opens a browser to a login URL provided by Azure Active Directory and authenticates a user
there with the authorization code flow, using PKCE (Proof Key for Code Exchange) internally to protect the code.

:keyword str authority: Authority of an Azure Active Directory endpoint, for example 'login.microsoftonline.com',
: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 str tenant_id: an Azure Active Directory tenant ID. Defaults to the 'organizations' tenant, which can
:keyword str tenant_id: an Azure Active Directory tenant ID. Defaults to the "organizations" tenant, which can
authenticate work or school accounts.
:keyword str client_id: Client ID of the Azure Active Directory application users will sign in to. If
unspecified, users will authenticate to an Azure development application.
:keyword str login_hint: a username suggestion to pre-fill the login page's username/email address field. A user
may still log in with a different username.
:keyword str redirect_uri: a redirect URI for the application identified by `client_id` as configured in Azure
Active Directory, for example "http://localhost:8400". This is only required when passing a value for
`client_id`, and must match a redirect URI in the application's registration. The credential must be able to
**client_id**, and must match a redirect URI in the application's registration. The credential must be able to
bind a socket to this URI.
:keyword AuthenticationRecord authentication_record: :class:`AuthenticationRecord` returned by :func:`authenticate`
:keyword bool disable_automatic_authentication: if True, :func:`get_token` will raise
Expand All @@ -51,7 +51,10 @@ 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).
:raises ValueError: invalid `redirect_uri`
: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**
"""

def __init__(self, **kwargs):
Expand Down Expand Up @@ -97,7 +100,7 @@ def _request_token(self, *scopes, **kwargs):
# get the url the user must visit to authenticate
scopes = list(scopes) # type: ignore
claims = kwargs.get("claims")
app = self._get_app()
app = self._get_app(**kwargs)
flow = app.initiate_auth_code_flow(
scopes,
redirect_uri=redirect_uri,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,26 @@ class CertificateCredential(ClientCredentialBase):
<https://docs.microsoft.com/azure/active-directory/develop/active-directory-certificate-credentials#register-your-certificate-with-microsoft-identity-platform>`_
for more information on configuring certificate authentication.

:param str tenant_id: ID of the service principal's tenant. Also called its 'directory' ID.
:param str tenant_id: ID of the service principal's tenant. Also called its "directory" ID.
:param str client_id: the service principal's client ID
:param str certificate_path: path to a PEM-encoded certificate file including the private key. If not provided,
`certificate_data` is required.
**certificate_data** is required.

: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.
the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts`
defines authorities for other clouds.
:keyword bytes certificate_data: the bytes of a certificate in PEM format, including the private key
: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.
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.
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.
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,18 @@
class ClientSecretCredential(ClientCredentialBase):
"""Authenticates as a service principal using a client secret.

:param str tenant_id: ID of the service principal's tenant. Also called its 'directory' ID.
:param str tenant_id: ID of the service principal's tenant. Also called its "directory" ID.
:param str client_id: the service principal's client ID
:param str client_secret: one of the service principal's client secrets

: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 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.
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ def get_token(self, *scopes, **kwargs):
)
return super(CloudShellCredential, self).get_token(*scopes, **kwargs)

def _acquire_token_silently(self, *scopes):
# type: (*str) -> Optional[AccessToken]
def _acquire_token_silently(self, *scopes, **kwargs):
# type: (*str, **Any) -> Optional[AccessToken]
return self._client.get_cached_token(*scopes)

def _request_token(self, *scopes, **kwargs):
Expand Down
Loading