Skip to content

Commit 039fa21

Browse files
jiasliqwordy
andauthored
[Core] Add workaround for cross-tenant authentication with Track 2 SDKs (#16797)
Co-authored-by: Feiyue Yu <iamyfy@163.com>
1 parent a3604d1 commit 039fa21

File tree

4 files changed

+28
-29
lines changed

4 files changed

+28
-29
lines changed

src/azure-cli-core/azure/cli/core/_profile.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,7 @@ def get_login_credentials(self, resource=None, subscription_id=None, aux_subscri
550550

551551
identity_type, identity_id = Profile._try_parse_msi_account_name(account)
552552

553+
# Make sure external_tenants_info only contains real external tenant (no current tenant).
553554
external_tenants_info = []
554555
if aux_tenants:
555556
external_tenants_info = [tenant for tenant in aux_tenants if tenant != account[_TENANT_ID]]
@@ -560,6 +561,10 @@ def get_login_credentials(self, resource=None, subscription_id=None, aux_subscri
560561
if sub[_TENANT_ID] != account[_TENANT_ID]:
561562
external_tenants_info.append(sub[_TENANT_ID])
562563

564+
if external_tenants_info and (identity_type or in_cloud_console()):
565+
raise CLIError("Cross-tenant authentication is not supported by managed identity and Cloud Shell. "
566+
"Please run `az login` with a user account or a service principal.")
567+
563568
if identity_type is None:
564569
def _retrieve_token(sdk_resource=None):
565570
# When called by

src/azure-cli-core/azure/cli/core/adal_authentication.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def _get_token(self, sdk_resource=None):
3535
"""
3636
external_tenant_tokens = None
3737
try:
38-
scheme, token, full_token = self._token_retriever(sdk_resource)
38+
scheme, token, token_entry = self._token_retriever(sdk_resource)
3939
if self._external_tenant_token_retriever:
4040
external_tenant_tokens = self._external_tenant_token_retriever(sdk_resource)
4141
except CLIError as err:
@@ -52,17 +52,20 @@ def _get_token(self, sdk_resource=None):
5252
except requests.exceptions.ConnectionError as err:
5353
raise CLIError('Please ensure you have network connection. Error detail: ' + str(err))
5454

55-
return scheme, token, full_token, external_tenant_tokens
55+
# scheme: str. The token scheme. Should always be 'Bearer'.
56+
# token: str. The raw access token.
57+
# token_entry: dict. The full token entry.
58+
# external_tenant_tokens: [(scheme: str, token: str, token_entry: dict), ...]
59+
return scheme, token, token_entry, external_tenant_tokens
5660

5761
def get_all_tokens(self, *scopes):
58-
scheme, token, full_token, external_tenant_tokens = self._get_token(_try_scopes_to_resource(scopes))
59-
return scheme, token, full_token, external_tenant_tokens
62+
return self._get_token(_try_scopes_to_resource(scopes))
6063

6164
# This method is exposed for Azure Core.
6265
def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument
6366
logger.debug("AdalAuthentication.get_token invoked by Track 2 SDK with scopes=%s", scopes)
6467

65-
_, token, full_token, _ = self._get_token(_try_scopes_to_resource(scopes))
68+
_, token, token_entry, _ = self._get_token(_try_scopes_to_resource(scopes))
6669

6770
# NEVER use expiresIn (expires_in) as the token is cached and expiresIn will be already out-of date
6871
# when being retrieved.
@@ -92,10 +95,10 @@ def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument
9295
# "_clientId": "22800c35-46c2-4210-b8a7-d8c3ec3b526f",
9396
# "_authority": "https://login.microsoftonline.com/54826b22-38d6-4fb2-bad9-b7b93a3e9c5a"
9497
# }
95-
if 'expiresOn' in full_token:
98+
if 'expiresOn' in token_entry:
9699
import datetime
97100
expires_on_timestamp = int(_timestamp(
98-
datetime.datetime.strptime(full_token['expiresOn'], '%Y-%m-%d %H:%M:%S.%f')))
101+
datetime.datetime.strptime(token_entry['expiresOn'], '%Y-%m-%d %H:%M:%S.%f')))
99102
return AccessToken(token, expires_on_timestamp)
100103

101104
# Cloud Shell (Managed Identity) token entry sample:
@@ -108,8 +111,8 @@ def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument
108111
# "resource": "https://management.core.windows.net/",
109112
# "token_type": "Bearer"
110113
# }
111-
if 'expires_on' in full_token:
112-
return AccessToken(token, int(full_token['expires_on']))
114+
if 'expires_on' in token_entry:
115+
return AccessToken(token, int(token_entry['expires_on']))
113116

114117
from azure.cli.core.azclierror import CLIInternalError
115118
raise CLIInternalError("No expiresOn or expires_on is available in the token entry.")

src/azure-cli-core/azure/cli/core/commands/client_factory.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,16 @@ def _get_mgmt_service_client(cli_ctx,
194194
client_kwargs.update(_prepare_client_kwargs_track2(cli_ctx))
195195
client_kwargs['credential_scopes'] = resource_to_scopes(resource)
196196

197+
# Track 2 currently lacks the ability to take external credentials.
198+
# https://github.com/Azure/azure-sdk-for-python/issues/8313
199+
# As a temporary workaround, manually add external tokens to 'x-ms-authorization-auxiliary' header.
200+
# https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/authenticate-multi-tenant
201+
if getattr(cred, "_external_tenant_token_retriever", None):
202+
*_, external_tenant_tokens = cred.get_all_tokens(*resource_to_scopes(resource))
203+
# Hard-code scheme to 'Bearer' as _BearerTokenCredentialPolicyBase._update_headers does.
204+
client_kwargs['headers']['x-ms-authorization-auxiliary'] = \
205+
', '.join("Bearer {}".format(t[1]) for t in external_tenant_tokens)
206+
197207
if subscription_bound:
198208
client = client_type(cred, subscription_id, **client_kwargs)
199209
else:

src/azure-cli/azure/cli/command_modules/vm/custom.py

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3387,31 +3387,13 @@ def create_image_version(cmd, resource_group_name, gallery_name, gallery_image_n
33873387
# print(target_regions)
33883388
from msrestazure.tools import resource_id, is_valid_resource_id
33893389
from azure.cli.core.commands.client_factory import get_subscription_id
3390-
from azure.cli.core._profile import Profile
33913390

33923391
ImageVersionPublishingProfile, GalleryArtifactSource, ManagedArtifact, ImageVersion, TargetRegion = cmd.get_models(
33933392
'GalleryImageVersionPublishingProfile', 'GalleryArtifactSource', 'ManagedArtifact', 'GalleryImageVersion',
33943393
'TargetRegion')
33953394
aux_subscriptions = _get_image_version_aux_subscription(managed_image, os_snapshot, data_snapshots)
33963395
client = _compute_client_factory(cmd.cli_ctx, aux_subscriptions=aux_subscriptions)
33973396

3398-
# Auxiliary tokens, pass it to init or operation
3399-
external_bearer_token = None
3400-
if aux_subscriptions:
3401-
profile = Profile(cli_ctx=cmd.cli_ctx)
3402-
resource = cmd.cli_ctx.cloud.endpoints.active_directory_resource_id
3403-
cred, _, _ = profile.get_login_credentials(resource=resource,
3404-
aux_subscriptions=aux_subscriptions)
3405-
_, _, _, external_tokens = cred.get_all_tokens('https://management.azure.com/.default')
3406-
if external_tokens:
3407-
external_token = external_tokens[0]
3408-
if len(external_token) >= 2:
3409-
external_bearer_token = external_token[0] + ' ' + external_token[1]
3410-
else:
3411-
logger.warning('Getting external tokens failed.')
3412-
else:
3413-
logger.warning('Getting external tokens failed.')
3414-
34153397
location = location or _get_resource_group_location(cmd.cli_ctx, resource_group_name)
34163398
end_of_life_date = fix_gallery_image_date_info(end_of_life_date)
34173399
if managed_image and not is_valid_resource_id(managed_image):
@@ -3468,8 +3450,7 @@ def create_image_version(cmd, resource_group_name, gallery_name, gallery_image_n
34683450
gallery_name=gallery_name,
34693451
gallery_image_name=gallery_image_name,
34703452
gallery_image_version_name=gallery_image_version,
3471-
gallery_image_version=image_version,
3472-
headers={'x-ms-authorization-auxiliary': external_bearer_token}
3453+
gallery_image_version=image_version
34733454
)
34743455

34753456

0 commit comments

Comments
 (0)