Skip to content

Commit 1745486

Browse files
author
jovieir
committed
Replay request to get the response body on HTTP Status 400
1 parent 3a2c33a commit 1745486

File tree

1 file changed

+34
-2
lines changed

1 file changed

+34
-2
lines changed

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

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# --------------------------------------------------------------------------------------------
55

66
import requests
7+
import json
78
from knack.log import get_logger
89
from msrestazure.azure_active_directory import MSIAuthentication
910

@@ -82,9 +83,24 @@ def set_token(self):
8283
logger.debug('throw requests.exceptions.HTTPError when doing MSIAuthentication: \n%s',
8384
traceback.format_exc())
8485
try:
86+
# RPs will often bubble up detailed error messages in the HTTP response body, making response.status and response.reason alone non-actionable.
87+
# Eg., The error 'Multiple user assigned identities exist, please specify the clientId / resourceId of the identity in the token request' is
88+
# returned with a response status of 400 and response.reason of 'Bad Request' only.
89+
#
90+
# We're still using the msrestazure library which is deprecated and not accepting new PRs.
91+
# As this library runs the get response without the stream=True param, the raw response that gets returned does not include the http response body.
92+
# result = requests.get(request_uri, params=payload, headers={'Metadata': 'true', 'User-Agent':self._user_agent})
93+
#
94+
# If we have a 400 http status, retry the operation to get the error. The err.request contains the request uri, payload and headers, so we can resend the request
95+
if err.response.status == 400:
96+
replay = replay_request(err)
97+
if len(replay.text) > 0:
98+
parsed_body_error = try_parse_json(replay.text)
99+
else:
100+
parsed_body_error = 'Unspecified'
85101
raise AzureResponseError('Failed to connect to MSI. Please make sure MSI is configured correctly.\n'
86-
'Get Token request returned http error: {}, reason: {}'
87-
.format(err.response.status, err.response.reason))
102+
'Get Token request returned http error: {}, reason: {}, details: {}'
103+
.format(err.response.status, err.response.reason, parsed_body_error))
88104
except AttributeError:
89105
raise AzureResponseError('Failed to connect to MSI. Please make sure MSI is configured correctly.\n'
90106
'Get Token request returned: {}'.format(err.response))
@@ -104,6 +120,22 @@ def get_auxiliary_tokens(self, *scopes, **kwargs): # pylint:disable=no-self-use
104120
simply return None."""
105121
return None
106122

123+
def replay_request(err):
124+
try:
125+
replay_request = requests.get(err.request.url, headers=err.request.headers, stream=True)
126+
return replay_request
127+
except requests.exceptions.HTTPError as err:
128+
logger.debug('throw requests.exceptions.HTTPError when doing MSIAuthentication: \n%s',
129+
traceback.format_exc())
130+
return err
131+
132+
def try_parse_json(json_string):
133+
try:
134+
parsed_json = json.loads(json_string)
135+
detailed_error = parsed_json['error_description']
136+
return detailed_error
137+
except json.JSONDecodeError:
138+
return None
107139

108140
def _normalize_expires_on(expires_on):
109141
"""

0 commit comments

Comments
 (0)