Skip to content

Commit 51e9af4

Browse files
committed
claims_challenge
1 parent 079bca2 commit 51e9af4

6 files changed

Lines changed: 46 additions & 15 deletions

File tree

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def login(self,
152152
allow_no_subscriptions=False,
153153
use_cert_sn_issuer=None,
154154
show_progress=False,
155-
**kwargs):
155+
claims_challenge=None):
156156
"""
157157
For service principal, `password` is a dict returned by ServicePrincipalAuth.build_credential
158158
"""
@@ -172,12 +172,12 @@ def login(self,
172172
use_device_code = True
173173

174174
if use_device_code:
175-
user_identity = identity.login_with_device_code(scopes=scopes, **kwargs)
175+
user_identity = identity.login_with_device_code(scopes=scopes, claims_challenge=claims_challenge)
176176
else:
177-
user_identity = identity.login_with_auth_code(scopes=scopes, **kwargs)
177+
user_identity = identity.login_with_auth_code(scopes=scopes, claims_challenge=claims_challenge)
178178
else:
179179
if not is_service_principal:
180-
user_identity = identity.login_with_username_password(username, password, scopes=scopes, **kwargs)
180+
user_identity = identity.login_with_username_password(username, password, scopes=scopes)
181181
else:
182182
identity.login_with_service_principal(username, password, scopes=scopes)
183183

src/azure-cli-core/azure/cli/core/auth/identity.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def _service_principal_store(self):
145145
Identity._service_principal_store_instance = ServicePrincipalStore(store)
146146
return Identity._service_principal_store_instance
147147

148-
def login_with_auth_code(self, scopes, **kwargs):
148+
def login_with_auth_code(self, scopes, claims_challenge=None):
149149
# Emit a warning to inform that a browser is opened.
150150
# Only show the path part of the URL and hide the query string.
151151

@@ -168,21 +168,25 @@ def _prompt_launching_ui(ui=None, **_):
168168
success_template=success_template, error_template=error_template,
169169
parent_window_handle=self._msal_app.CONSOLE_WINDOW_HANDLE, on_before_launching_ui=_prompt_launching_ui,
170170
enable_msa_passthrough=True,
171-
**kwargs)
171+
claims_challenge=claims_challenge
172+
)
172173
return check_result(result)
173174

174-
def login_with_device_code(self, scopes, **kwargs):
175-
flow = self._msal_app.initiate_device_flow(scopes, **kwargs)
175+
def login_with_device_code(self, scopes, claims_challenge=None):
176+
flow = self._msal_app.initiate_device_flow(scopes, claims_challenge=claims_challenge)
176177
if "user_code" not in flow:
177178
raise ValueError(
178179
"Fail to create device flow. Err: %s" % json.dumps(flow, indent=4))
179180
from azure.cli.core.style import print_styled_text, Style
180181
print_styled_text((Style.WARNING, flow["message"]), file=sys.stderr)
181-
result = self._msal_app.acquire_token_by_device_flow(flow, **kwargs) # By default it will block
182+
result = self._msal_app.acquire_token_by_device_flow(
183+
flow,
184+
claims_challenge=claims_challenge
185+
) # By default, it will block
182186
return check_result(result)
183187

184-
def login_with_username_password(self, username, password, scopes, **kwargs):
185-
result = self._msal_app.acquire_token_by_username_password(username, password, scopes, **kwargs)
188+
def login_with_username_password(self, username, password, scopes):
189+
result = self._msal_app.acquire_token_by_username_password(username, password, scopes)
186190
return check_result(result)
187191

188192
def login_with_service_principal(self, client_id, credential, scopes):

src/azure-cli-core/azure/cli/core/auth/util.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,17 @@ def _generate_login_command(scopes=None, claims_challenge=None):
5858

5959
# Rejected by Conditional Access policy, like MFA
6060
if scopes:
61-
login_command.append('--scope {}'.format(' '.join(scopes)))
61+
# We can use * unpacking: https://peps.python.org/pep-0448/:
62+
# login_command.extend(['--scope', *scopes])
63+
# but we keep it simple to read.
64+
login_command.append('--scope')
65+
login_command.extend(scopes)
6266

6367
# Rejected by CAE
6468
if claims_challenge:
69+
from azure.cli.core.util import b64encode
70+
claims_challenge_encoded = b64encode(claims_challenge)
71+
login_command.extend(['--claims', claims_challenge_encoded])
6572
# Explicit logout is needed: https://github.com/AzureAD/microsoft-authentication-library-for-python/issues/335
6673
return 'az logout\n' + ' '.join(login_command)
6774

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -603,15 +603,25 @@ def shell_safe_json_parse(json_or_dict_string, preserve_order=False, strict=True
603603

604604
def b64encode(s):
605605
"""
606-
Encodes a string to base64 on 2.x and 3.x
606+
Encodes a string to a base64 string.
607607
:param str s: latin_1 encoded string
608608
:return: base64 encoded string
609609
:rtype: str
610610
"""
611611
encoded = base64.b64encode(s.encode("latin-1"))
612-
return encoded if encoded is str else encoded.decode('latin-1')
612+
return encoded.decode('latin-1')
613613

614614

615+
def b64decode(s):
616+
"""
617+
Decodes a base64 string to a string.
618+
:param str s: latin_1 encoded base64 string
619+
:return: decoded string
620+
:rtype: str
621+
"""
622+
encoded = base64.b64decode(s.encode("latin-1"))
623+
return encoded.decode('latin-1')
624+
615625
def b64_to_hex(s):
616626
"""
617627
Decodes a string to base64 on 2.x and 3.x

src/azure-cli/azure/cli/command_modules/profile/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ def load_arguments(self, command):
5555
c.argument('allow_no_subscriptions', action='store_true',
5656
help="Support accessing tenants without subscriptions. It's useful to run "
5757
"tenant-level commands, such as 'az ad'.")
58+
c.argument('claims_challenge',
59+
help="Base64-encoded claims challenge requested by a resource API in the "
60+
"WWW-Authenticate header.")
5861
c.ignore('_subscription') # hide the global subscription parameter
5962

6063
# Device code flow

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ def account_clear(cmd):
122122

123123
# pylint: disable=too-many-branches, too-many-locals
124124
def login(cmd, username=None, password=None, tenant=None, scopes=None, allow_no_subscriptions=False,
125+
claims_challenge=None,
125126
# Device code flow
126127
use_device_code=False,
127128
# Service principal
@@ -148,6 +149,10 @@ def login(cmd, username=None, password=None, tenant=None, scopes=None, allow_no_
148149
else:
149150
logger.warning(USERNAME_PASSWORD_DEPRECATION_WARNING_OTHER_CLOUD)
150151

152+
if claims_challenge:
153+
from azure.cli.core.util import b64decode
154+
claims_challenge=b64decode(claims_challenge)
155+
151156
interactive = False
152157

153158
profile = Profile(cli_ctx=cmd.cli_ctx)
@@ -194,7 +199,9 @@ def login(cmd, username=None, password=None, tenant=None, scopes=None, allow_no_
194199
use_device_code=use_device_code,
195200
allow_no_subscriptions=allow_no_subscriptions,
196201
use_cert_sn_issuer=use_cert_sn_issuer,
197-
show_progress=select_subscription)
202+
show_progress=select_subscription,
203+
claims_challenge=claims_challenge
204+
)
198205

199206
# Launch interactive account selection. No JSON output.
200207
if select_subscription:

0 commit comments

Comments
 (0)