Skip to content

Actionable exception from ADFS username password flow #456

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions msal/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -1417,12 +1417,18 @@ def acquire_token_by_username_password(
user_realm_result = self.authority.user_realm_discovery(
username, correlation_id=headers[msal.telemetry.CLIENT_REQUEST_ID])
if user_realm_result.get("account_type") == "Federated":
response = _clean_up(self._acquire_token_by_username_password_federated(
user_realm_result, username, password, scopes=scopes,
data=data,
headers=headers, **kwargs))
telemetry_context.update_telemetry(response)
return response
try:
response = _clean_up(self._acquire_token_by_username_password_federated(
user_realm_result, username, password, scopes=scopes,
data=data,
headers=headers, **kwargs))
except (ValueError, RuntimeError):
raise RuntimeError(
"ADFS is not configured properly. "
"Consider use acquire_token_interactive() instead.")
Comment on lines +1426 to +1428
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we include the original exception as an "inner" exception?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, the "inner" exception would show up in Python3's error trace already, therefore visible by default.

However, I would expect Azure CLI to do this, to have an actionable guidance most suitable in the Azure CLI's context:

try:
    result = app.acquire_token_by_username_password(...)
except RuntimeError:
    raise CLIError('Username password login failed. You may consider using "az login" instead.')

Copy link
Contributor

@jiasli jiasli Feb 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't "Username password login failed" be too vague? This exception chaining pattern is difficult for Azure CLI to get the inner exception message, like that of the original (ValueError, RuntimeError) and print it in the terminal.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can probably print the exception you caught. It would contain some (perhaps too much) info. Alas, it is hard to strike a balance here. :-(

try:
    result = app.acquire_token_by_username_password(...)
except RuntimeError as e:
    raise CLIError('Username password login failed: {}. You may consider using "az login" instead.'.format(e))

else:
telemetry_context.update_telemetry(response)
return response
response = _clean_up(self.client.obtain_token_by_username_password(
username, password, scope=scopes,
headers=headers,
Expand Down
4 changes: 2 additions & 2 deletions msal/wstrust_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ def send_request(
soap_action = Mex.ACTION_2005
elif '/trust/13/usernamemixed' in endpoint_address:
soap_action = Mex.ACTION_13
assert soap_action in (Mex.ACTION_13, Mex.ACTION_2005), ( # A loose check here
"Unsupported soap action: %s" % soap_action)
if soap_action not in (Mex.ACTION_13, Mex.ACTION_2005):
raise ValueError("Unsupported soap action: %s" % soap_action)
data = _build_rst(
username, password, cloud_audience_urn, endpoint_address, soap_action)
resp = http_client.post(endpoint_address, data=data, headers={
Expand Down