Skip to content

Commit 4fbe208

Browse files
authored
Credentials accept tenant_id argument to get_token (#19602)
1 parent 4134479 commit 4fbe208

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1965
-311
lines changed

sdk/identity/azure-identity/azure/identity/_constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,5 @@ class EnvironmentVariables:
4444
MSI_SECRET = "MSI_SECRET"
4545

4646
AZURE_AUTHORITY_HOST = "AZURE_AUTHORITY_HOST"
47+
AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION = "AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION"
4748
AZURE_REGIONAL_AUTHORITY_NAME = "AZURE_REGIONAL_AUTHORITY_NAME"

sdk/identity/azure-identity/azure/identity/_credentials/app_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ def get_token(self, *scopes, **kwargs):
3838
)
3939
return super(AppServiceCredential, self).get_token(*scopes, **kwargs)
4040

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

4545
def _request_token(self, *scopes, **kwargs):

sdk/identity/azure-identity/azure/identity/_credentials/application.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ class AzureApplicationCredential(ChainedTokenCredential):
4848
<https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview>`_ for an overview of
4949
managed identities.
5050
51+
:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
52+
the application or user is registered in. When False, which is the default, the credential will acquire tokens
53+
only from the tenant specified by **AZURE_TENANT_ID**. This argument doesn't apply to managed identity
54+
authentication.
5155
:keyword str authority: Authority of an Azure Active Directory endpoint, for example "login.microsoftonline.com",
5256
the authority for Azure Public Cloud, which is the default when no value is given for this keyword argument or
5357
environment variable AZURE_AUTHORITY_HOST. :class:`~azure.identity.AzureAuthorityHosts` defines authorities for

sdk/identity/azure-identity/azure/identity/_credentials/authorization_code.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,29 @@
1010

1111
if TYPE_CHECKING:
1212
# pylint:disable=unused-import,ungrouped-imports
13-
from typing import Any, Iterable, Optional
13+
from typing import Any, Optional
1414
from azure.core.credentials import AccessToken
1515

1616

1717
class AuthorizationCodeCredential(GetTokenMixin):
1818
"""Authenticates by redeeming an authorization code previously obtained from Azure Active Directory.
1919
20-
See https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-auth-code-flow for more information
20+
See `Azure Active Directory documentation
21+
<https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-auth-code-flow>`_ for more information
2122
about the authentication flow.
2223
23-
:param str tenant_id: ID of the application's Azure Active Directory tenant. Also called its 'directory' ID.
24+
:param str tenant_id: ID of the application's Azure Active Directory tenant. Also called its "directory" ID.
2425
:param str client_id: the application's client ID
2526
:param str authorization_code: the authorization code from the user's log-in
2627
:param str redirect_uri: The application's redirect URI. Must match the URI used to request the authorization code.
2728
28-
:keyword str authority: Authority of an Azure Active Directory endpoint, for example 'login.microsoftonline.com',
29-
the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts`
30-
defines authorities for other clouds.
29+
:keyword str authority: Authority of an Azure Active Directory endpoint, for example "login.microsoftonline.com",
30+
the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts`
31+
defines authorities for other clouds.
3132
:keyword str client_secret: One of the application's client secrets. Required only for web apps and web APIs.
33+
:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
34+
the user is registered in. When False, which is the default, the credential will acquire tokens only from the
35+
user's home tenant or the tenant specified by **tenant_id**.
3236
"""
3337

3438
def __init__(self, tenant_id, client_id, authorization_code, redirect_uri, **kwargs):
@@ -51,16 +55,20 @@ def get_token(self, *scopes, **kwargs):
5155
redeeming the authorization code.
5256
5357
:param str scopes: desired scopes for the access token. This method requires at least one scope.
58+
:keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication**
59+
is False, specifying a tenant with this argument may raise an exception.
60+
5461
:rtype: :class:`azure.core.credentials.AccessToken`
5562
:raises ~azure.core.exceptions.ClientAuthenticationError: authentication failed. The error's ``message``
5663
attribute gives a reason. Any error response from Azure Active Directory is available as the error's
5764
``response`` attribute.
5865
"""
59-
return super(AuthorizationCodeCredential, self).get_token(*scopes)
66+
# pylint:disable=useless-super-delegation
67+
return super(AuthorizationCodeCredential, self).get_token(*scopes, **kwargs)
6068

61-
def _acquire_token_silently(self, *scopes):
62-
# type: (*str) -> Optional[AccessToken]
63-
return self._client.get_cached_access_token(scopes)
69+
def _acquire_token_silently(self, *scopes, **kwargs):
70+
# type: (*str, **Any) -> Optional[AccessToken]
71+
return self._client.get_cached_access_token(scopes, **kwargs)
6472

6573
def _request_token(self, *scopes, **kwargs):
6674
# type: (*str, **Any) -> AccessToken

sdk/identity/azure-identity/azure/identity/_credentials/azure_arc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ def get_token(self, *scopes, **kwargs):
5858
)
5959
return super(AzureArcCredential, self).get_token(*scopes, **kwargs)
6060

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

6565
def _request_token(self, *scopes, **kwargs):

sdk/identity/azure-identity/azure/identity/_credentials/azure_cli.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from azure.core.exceptions import ClientAuthenticationError
1919

2020
from .. import CredentialUnavailableError
21-
from .._internal import _scopes_to_resource
21+
from .._internal import _scopes_to_resource, resolve_tenant
2222
from .._internal.decorators import log_get_token
2323

2424
if TYPE_CHECKING:
@@ -35,17 +35,27 @@ class AzureCliCredential(object):
3535
"""Authenticates by requesting a token from the Azure CLI.
3636
3737
This requires previously logging in to Azure via "az login", and will use the CLI's currently logged in identity.
38+
39+
:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
40+
the identity logged in to the Azure CLI is registered in. When False, which is the default, the credential will
41+
acquire tokens only from the tenant of the Azure CLI's active subscription.
3842
"""
3943

44+
def __init__(self, **kwargs):
45+
self._allow_multitenant = kwargs.get("allow_multitenant_authentication", False)
46+
4047
@log_get_token("AzureCliCredential")
41-
def get_token(self, *scopes, **kwargs): # pylint:disable=no-self-use,unused-argument
48+
def get_token(self, *scopes, **kwargs):
4249
# type: (*str, **Any) -> AccessToken
4350
"""Request an access token for `scopes`.
4451
4552
This method is called automatically by Azure SDK clients. Applications calling this method directly must
4653
also handle token caching because this credential doesn't cache the tokens it acquires.
4754
4855
:param str scopes: desired scope for the access token. This credential allows only one scope per request.
56+
:keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication**
57+
is False, specifying a tenant with this argument may raise an exception.
58+
4959
:rtype: :class:`azure.core.credentials.AccessToken`
5060
5161
:raises ~azure.identity.CredentialUnavailableError: the credential was unable to invoke the Azure CLI.
@@ -54,7 +64,11 @@ def get_token(self, *scopes, **kwargs): # pylint:disable=no-self-use,unused-arg
5464
"""
5565

5666
resource = _scopes_to_resource(*scopes)
57-
output = _run_command(COMMAND_LINE.format(resource))
67+
command = COMMAND_LINE.format(resource)
68+
tenant = resolve_tenant("", self._allow_multitenant, **kwargs)
69+
if tenant:
70+
command += " --tenant " + tenant
71+
output = _run_command(command)
5872

5973
token = parse_token(output)
6074
if not token:

sdk/identity/azure-identity/azure/identity/_credentials/azure_powershell.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
from .azure_cli import get_safe_working_dir
1818
from .. import CredentialUnavailableError
19-
from .._internal import _scopes_to_resource
19+
from .._internal import _scopes_to_resource, resolve_tenant
2020
from .._internal.decorators import log_get_token
2121

2222
if TYPE_CHECKING:
@@ -41,7 +41,7 @@
4141
exit
4242
}}
4343
44-
$token = Get-AzAccessToken -ResourceUrl '{}'
44+
$token = Get-AzAccessToken -ResourceUrl '{}'{}
4545
4646
Write-Output "`nazsdk%$($token.Token)%$($token.ExpiresOn.ToUnixTimeSeconds())`n"
4747
"""
@@ -51,26 +51,37 @@ class AzurePowerShellCredential(object):
5151
"""Authenticates by requesting a token from Azure PowerShell.
5252
5353
This requires previously logging in to Azure via "Connect-AzAccount", and will use the currently logged in identity.
54+
55+
:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
56+
the identity logged in to Azure PowerShell is registered in. When False, which is the default, the credential
57+
will acquire tokens only from the tenant of Azure PowerShell's active subscription.
5458
"""
5559

60+
def __init__(self, **kwargs):
61+
# type: (**Any) -> None
62+
self._allow_multitenant = kwargs.get("allow_multitenant_authentication", False)
63+
5664
@log_get_token("AzurePowerShellCredential")
57-
def get_token(self, *scopes, **kwargs): # pylint:disable=no-self-use,unused-argument
65+
def get_token(self, *scopes, **kwargs):
5866
# type: (*str, **Any) -> AccessToken
5967
"""Request an access token for `scopes`.
6068
6169
This method is called automatically by Azure SDK clients. Applications calling this method directly must
6270
also handle token caching because this credential doesn't cache the tokens it acquires.
6371
6472
:param str scopes: desired scope for the access token. This credential allows only one scope per request.
73+
:keyword str tenant_id: optional tenant to include in the token request. If **allow_multitenant_authentication**
74+
is False, specifying a tenant with this argument may raise an exception.
75+
6576
:rtype: :class:`azure.core.credentials.AccessToken`
6677
6778
:raises ~azure.identity.CredentialUnavailableError: the credential was unable to invoke Azure PowerShell, or
6879
no account is authenticated
6980
:raises ~azure.core.exceptions.ClientAuthenticationError: the credential invoked Azure PowerShell but didn't
7081
receive an access token
7182
"""
72-
73-
command_line = get_command_line(scopes)
83+
tenant_id = resolve_tenant("", self._allow_multitenant, **kwargs)
84+
command_line = get_command_line(scopes, tenant_id)
7485
output = run_command_line(command_line)
7586
token = parse_token(output)
7687
return token
@@ -128,10 +139,14 @@ def parse_token(output):
128139
raise ClientAuthenticationError(message='Unexpected output from Get-AzAccessToken: "{}"'.format(output))
129140

130141

131-
def get_command_line(scopes):
132-
# type: (Tuple) -> List[str]
142+
def get_command_line(scopes, tenant_id):
143+
# type: (Tuple, str) -> List[str]
144+
if tenant_id:
145+
tenant_argument = " -TenantId " + tenant_id
146+
else:
147+
tenant_argument = ""
133148
resource = _scopes_to_resource(*scopes)
134-
script = SCRIPT.format(NO_AZ_ACCOUNT_MODULE, resource)
149+
script = SCRIPT.format(NO_AZ_ACCOUNT_MODULE, resource, tenant_argument)
135150
encoded_script = base64.b64encode(script.encode("utf-16-le")).decode()
136151

137152
command = "pwsh -NonInteractive -EncodedCommand " + encoded_script

sdk/identity/azure-identity/azure/identity/_credentials/browser.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,18 @@ class InteractiveBrowserCredential(InteractiveCredential):
3131
:func:`~get_token` opens a browser to a login URL provided by Azure Active Directory and authenticates a user
3232
there with the authorization code flow, using PKCE (Proof Key for Code Exchange) internally to protect the code.
3333
34-
:keyword str authority: Authority of an Azure Active Directory endpoint, for example 'login.microsoftonline.com',
34+
:keyword str authority: Authority of an Azure Active Directory endpoint, for example "login.microsoftonline.com",
3535
the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts`
3636
defines authorities for other clouds.
37-
:keyword str tenant_id: an Azure Active Directory tenant ID. Defaults to the 'organizations' tenant, which can
37+
:keyword str tenant_id: an Azure Active Directory tenant ID. Defaults to the "organizations" tenant, which can
3838
authenticate work or school accounts.
3939
:keyword str client_id: Client ID of the Azure Active Directory application users will sign in to. If
4040
unspecified, users will authenticate to an Azure development application.
4141
:keyword str login_hint: a username suggestion to pre-fill the login page's username/email address field. A user
4242
may still log in with a different username.
4343
:keyword str redirect_uri: a redirect URI for the application identified by `client_id` as configured in Azure
4444
Active Directory, for example "http://localhost:8400". This is only required when passing a value for
45-
`client_id`, and must match a redirect URI in the application's registration. The credential must be able to
45+
**client_id**, and must match a redirect URI in the application's registration. The credential must be able to
4646
bind a socket to this URI.
4747
:keyword AuthenticationRecord authentication_record: :class:`AuthenticationRecord` returned by :func:`authenticate`
4848
:keyword bool disable_automatic_authentication: if True, :func:`get_token` will raise
@@ -51,7 +51,10 @@ class InteractiveBrowserCredential(InteractiveCredential):
5151
will cache tokens in memory.
5252
:paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions
5353
:keyword int timeout: seconds to wait for the user to complete authentication. Defaults to 300 (5 minutes).
54-
:raises ValueError: invalid `redirect_uri`
54+
:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
55+
the user is registered in. When False, which is the default, the credential will acquire tokens only from the
56+
user's home tenant or the tenant specified by **tenant_id**.
57+
:raises ValueError: invalid **redirect_uri**
5558
"""
5659

5760
def __init__(self, **kwargs):
@@ -97,7 +100,7 @@ def _request_token(self, *scopes, **kwargs):
97100
# get the url the user must visit to authenticate
98101
scopes = list(scopes) # type: ignore
99102
claims = kwargs.get("claims")
100-
app = self._get_app()
103+
app = self._get_app(**kwargs)
101104
flow = app.initiate_auth_code_flow(
102105
scopes,
103106
redirect_uri=redirect_uri,

sdk/identity/azure-identity/azure/identity/_credentials/certificate.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,26 @@ class CertificateCredential(ClientCredentialBase):
2626
<https://docs.microsoft.com/azure/active-directory/develop/active-directory-certificate-credentials#register-your-certificate-with-microsoft-identity-platform>`_
2727
for more information on configuring certificate authentication.
2828
29-
:param str tenant_id: ID of the service principal's tenant. Also called its 'directory' ID.
29+
:param str tenant_id: ID of the service principal's tenant. Also called its "directory" ID.
3030
:param str client_id: the service principal's client ID
3131
:param str certificate_path: path to a PEM-encoded certificate file including the private key. If not provided,
32-
`certificate_data` is required.
32+
**certificate_data** is required.
3333
3434
:keyword str authority: Authority of an Azure Active Directory endpoint, for example 'login.microsoftonline.com',
35-
the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts`
36-
defines authorities for other clouds.
35+
the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts`
36+
defines authorities for other clouds.
3737
:keyword bytes certificate_data: the bytes of a certificate in PEM format, including the private key
3838
:keyword password: The certificate's password. If a unicode string, it will be encoded as UTF-8. If the certificate
39-
requires a different encoding, pass appropriately encoded bytes instead.
39+
requires a different encoding, pass appropriately encoded bytes instead.
4040
:paramtype password: str or bytes
41+
:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
42+
the application is registered in. When False, which is the default, the credential will acquire tokens only from
43+
the tenant specified by **tenant_id**.
4144
:keyword bool send_certificate_chain: if True, the credential will send the public certificate chain in the x5c
42-
header of each token request's JWT. This is required for Subject Name/Issuer (SNI) authentication. Defaults
43-
to False.
45+
header of each token request's JWT. This is required for Subject Name/Issuer (SNI) authentication. Defaults to
46+
False.
4447
:keyword cache_persistence_options: configuration for persistent token caching. If unspecified, the credential
45-
will cache tokens in memory.
48+
will cache tokens in memory.
4649
:paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions
4750
:keyword ~azure.identity.RegionalAuthority regional_authority: a :class:`~azure.identity.RegionalAuthority` to
4851
which the credential will authenticate. This argument should be used only by applications deployed to Azure

sdk/identity/azure-identity/azure/identity/_credentials/client_secret.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,18 @@
1414
class ClientSecretCredential(ClientCredentialBase):
1515
"""Authenticates as a service principal using a client secret.
1616
17-
:param str tenant_id: ID of the service principal's tenant. Also called its 'directory' ID.
17+
:param str tenant_id: ID of the service principal's tenant. Also called its "directory" ID.
1818
:param str client_id: the service principal's client ID
1919
:param str client_secret: one of the service principal's client secrets
2020
21-
:keyword str authority: Authority of an Azure Active Directory endpoint, for example 'login.microsoftonline.com',
22-
the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts`
23-
defines authorities for other clouds.
21+
:keyword str authority: Authority of an Azure Active Directory endpoint, for example "login.microsoftonline.com",
22+
the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts`
23+
defines authorities for other clouds.
24+
:keyword bool allow_multitenant_authentication: when True, enables the credential to acquire tokens from any tenant
25+
the application is registered in. When False, which is the default, the credential will acquire tokens only from
26+
the tenant specified by **tenant_id**.
2427
:keyword cache_persistence_options: configuration for persistent token caching. If unspecified, the credential
25-
will cache tokens in memory.
28+
will cache tokens in memory.
2629
:paramtype cache_persistence_options: ~azure.identity.TokenCachePersistenceOptions
2730
:keyword ~azure.identity.RegionalAuthority regional_authority: a :class:`~azure.identity.RegionalAuthority` to
2831
which the credential will authenticate. This argument should be used only by applications deployed to Azure

0 commit comments

Comments
 (0)