Skip to content

Commit 2af955e

Browse files
Copilotweikanglim
andcommitted
Include scopes in az login command for claims challenge error messages
Co-authored-by: weikanglim <[email protected]>
1 parent a9bd2a7 commit 2af955e

File tree

4 files changed

+48
-6
lines changed

4 files changed

+48
-6
lines changed

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,10 @@ def get_token(
115115
receive an access token.
116116
"""
117117
if claims and claims.strip():
118-
raise CredentialUnavailableError(f"Failed to get token. Run az login --claims-challenge {claims}")
118+
login_cmd = f"az login --claims-challenge {claims}"
119+
if scopes:
120+
login_cmd += f" --scope {scopes[0]}"
121+
raise CredentialUnavailableError(f"Failed to get token. Run {login_cmd}")
119122

120123
options: TokenRequestOptions = {}
121124
if tenant_id:
@@ -147,7 +150,10 @@ def get_token_info(self, *scopes: str, options: Optional[TokenRequestOptions] =
147150
"""
148151
claims_value = options.get("claims") if options else None
149152
if claims_value and claims_value.strip():
150-
raise CredentialUnavailableError(f"Failed to get token. Run az login --claims-challenge {claims_value}")
153+
login_cmd = f"az login --claims-challenge {claims_value}"
154+
if scopes:
155+
login_cmd += f" --scope {scopes[0]}"
156+
raise CredentialUnavailableError(f"Failed to get token. Run {login_cmd}")
151157
return self._get_token_base(*scopes, options=options)
152158

153159
def _get_token_base(

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,10 @@ async def get_token(
106106
receive an access token.
107107
"""
108108
if claims and claims.strip():
109-
raise CredentialUnavailableError(f"Failed to get token. Run az login --claims-challenge {claims}")
109+
login_cmd = f"az login --claims-challenge {claims}"
110+
if scopes:
111+
login_cmd += f" --scope {scopes[0]}"
112+
raise CredentialUnavailableError(f"Failed to get token. Run {login_cmd}")
110113

111114
# only ProactorEventLoop supports subprocesses on Windows (and it isn't the default loop on Python < 3.8)
112115
if sys.platform.startswith("win") and not isinstance(asyncio.get_event_loop(), asyncio.ProactorEventLoop):
@@ -142,7 +145,10 @@ async def get_token_info(self, *scopes: str, options: Optional[TokenRequestOptio
142145
"""
143146
claims_value = options.get("claims") if options else None
144147
if claims_value and claims_value.strip():
145-
raise CredentialUnavailableError(f"Failed to get token. Run az login --claims-challenge {claims_value}")
148+
login_cmd = f"az login --claims-challenge {claims_value}"
149+
if scopes:
150+
login_cmd += f" --scope {scopes[0]}"
151+
raise CredentialUnavailableError(f"Failed to get token. Run {login_cmd}")
146152
# only ProactorEventLoop supports subprocesses on Windows (and it isn't the default loop on Python < 3.8)
147153
if sys.platform.startswith("win") and not isinstance(asyncio.get_event_loop(), asyncio.ProactorEventLoop):
148154
return _SyncAzureCliCredential().get_token_info(*scopes, options=options)

sdk/identity/azure-identity/tests/test_cli_credential.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ def test_claims_challenge_raises_error(get_token_method):
402402
"""The credential should raise CredentialUnavailableError when claims challenge is provided"""
403403

404404
claims = "test-claims-challenge"
405-
expected_message = f"Failed to get token. Run az login --claims-challenge {claims}"
405+
expected_message = f"Failed to get token. Run az login --claims-challenge {claims} --scope scope"
406406

407407
if get_token_method == "get_token":
408408
with pytest.raises(CredentialUnavailableError, match=re.escape(expected_message)):
@@ -412,6 +412,21 @@ def test_claims_challenge_raises_error(get_token_method):
412412
AzureCliCredential().get_token_info("scope", options={"claims": claims})
413413

414414

415+
@pytest.mark.parametrize("get_token_method", GET_TOKEN_METHODS)
416+
def test_claims_challenge_without_scopes(get_token_method):
417+
"""The credential should raise CredentialUnavailableError with appropriate message even when no scopes provided"""
418+
419+
claims = "test-claims-challenge"
420+
expected_message = f"Failed to get token. Run az login --claims-challenge {claims}"
421+
422+
if get_token_method == "get_token":
423+
with pytest.raises(CredentialUnavailableError, match=re.escape(expected_message)):
424+
AzureCliCredential().get_token(claims=claims) # No scopes provided
425+
else: # get_token_info
426+
with pytest.raises(CredentialUnavailableError, match=re.escape(expected_message)):
427+
AzureCliCredential().get_token_info(options={"claims": claims}) # No scopes provided
428+
429+
415430
@pytest.mark.parametrize("get_token_method", GET_TOKEN_METHODS)
416431
def test_empty_claims_does_not_raise_error(get_token_method):
417432
"""The credential should not raise error when claims parameter is empty or None"""

sdk/identity/azure-identity/tests/test_cli_credential_async.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ async def test_claims_challenge_raises_error(get_token_method):
396396
"""The credential should raise CredentialUnavailableError when claims challenge is provided"""
397397

398398
claims = "test-claims-challenge"
399-
expected_message = f"Failed to get token. Run az login --claims-challenge {claims}"
399+
expected_message = f"Failed to get token. Run az login --claims-challenge {claims} --scope scope"
400400

401401
if get_token_method == "get_token":
402402
with pytest.raises(CredentialUnavailableError, match=re.escape(expected_message)):
@@ -406,6 +406,21 @@ async def test_claims_challenge_raises_error(get_token_method):
406406
await AzureCliCredential().get_token_info("scope", options={"claims": claims})
407407

408408

409+
@pytest.mark.parametrize("get_token_method", GET_TOKEN_METHODS)
410+
async def test_claims_challenge_without_scopes(get_token_method):
411+
"""The credential should raise CredentialUnavailableError with appropriate message even when no scopes provided"""
412+
413+
claims = "test-claims-challenge"
414+
expected_message = f"Failed to get token. Run az login --claims-challenge {claims}"
415+
416+
if get_token_method == "get_token":
417+
with pytest.raises(CredentialUnavailableError, match=re.escape(expected_message)):
418+
await AzureCliCredential().get_token(claims=claims) # No scopes provided
419+
else: # get_token_info
420+
with pytest.raises(CredentialUnavailableError, match=re.escape(expected_message)):
421+
await AzureCliCredential().get_token_info(options={"claims": claims}) # No scopes provided
422+
423+
409424
@pytest.mark.parametrize("get_token_method", GET_TOKEN_METHODS)
410425
async def test_empty_claims_does_not_raise_error(get_token_method):
411426
"""The credential should not raise error when claims parameter is empty or None"""

0 commit comments

Comments
 (0)