-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Description
Track 1 SDK - working
Previously in Track 1 SDK, the corresponding name of scopes is resource. The SDK itself doesn't maintain/know the resource. resource is provide by Azure CLI and used in token retrieval.
mgmt-plane - azure/cli/core/commands/client_factory.py:
resource = resource or cli_ctx.cloud.endpoints.active_directory_resource_iddata-plane - azure/cli/command_modules/role/_client_factory.py:
cred, _, tenant_id = profile.get_login_credentials(
resource=cli_ctx.cloud.endpoints.active_directory_graph_resource_id)Then the resource is used in get_login_credentials:
def get_login_credentials(self, resource=None, subscription_id=None, aux_subscriptions=None, aux_tenants=None):
if aux_tenants and aux_subscriptions:and saved in the closure for _retrieve_token.
Later on, when signed_session is called, it doesn't accept a resource and uses the one passed to get_login_credentials instead.
def signed_session(self, session=None):Track 2 SDK - not working
In Track 2 SDK, the scopes is managed by the SDK itself:
azure-sdk-for-python/sdk/storage/azure-mgmt-storage/azure/mgmt/storage/_configuration.py
Lines 46 to 47 in 64e0126
| self.credential_scopes = ['https://management.azure.com/.default'] | |
| self.credential_scopes.extend(kwargs.pop('credential_scopes', [])) |
Then scopes is passed to get_token
azure-sdk-for-python/sdk/core/azure-core/azure/core/pipeline/policies/_authentication.py
Line 93 in 5fdeae5
| self._token = self._credential.get_token(*self._scopes) |
If used in a sovereign cloud (like China cloud), the client needs to be created as
StorageManagementClient(credential_scopes=['https://management.core.chinacloudapi.cn/.default'])Thus, credential_scopes becomes
['https://management.azure.com/.default', 'https://management.core.chinacloudapi.cn/.default']which is not valid according to OAuth 2.0 authorization code flow, because it contains more than one resource:
| Parameter | Type | Description |
|---|---|---|
scope |
optional | A space-separated list of scopes. The scopes must all be from a single resource, along with OIDC scopes (profile, openid, email). |
This can also be verified with
az account get-access-token --scopes https://management.core.chinacloudapi.cn/.default https://management.azure.com/.default --debug
azure.core.pipeline.policies.http_logging_policy : Request URL: 'https://login.microsoftonline.com/54826b22-38d6-4fb2-bad9-b7b93a3e9c5a/oauth2/v2.0/token'
azure.core.pipeline.policies.http_logging_policy : Request method: 'POST'
azure.core.pipeline.policies.http_logging_policy : Response status: 400
msal.application : Refresh failed. invalid_scope: AADSTS70011: The provided request must include a 'scope' input parameter. The provided value for the input parameter 'scope' is not valid. The scope https://management.azure.com/.default https://management.core.chinacloudapi.cn/.default offline_access openid profile is not valid. .default scope can't be combined with resource-specific scopes.
Why this issue doesn't happen now (Track 2 SDK + ADAL)
This is because when get_token from Azure CLI is called, scopes is totally unused/discarded, thus forcing the Track 2 SDK client to use the resource provided by Azure CLI in get_login_credentials.
In the MSAL integration process (Azure/azure-cli#14690), I try to honor the scopes provided by SDK and hit this issue.
Solution
The SDK needs to override the default credential_scopes if credential_scopes is provided in kwargs, so that the default value 'https://management.azure.com/.default' doesn't appear in self.credential_scopes.
A fix can be like this:
self.credential_scopes = kwargs.pop('credential_scopes', ['https://management.azure.com/.default'])whose value is
['https://management.core.chinacloudapi.cn/.default']