Skip to content

Commit 34601dc

Browse files
committed
Fix for 5033 - MSI response json parse failure error code
1 parent 01e79b1 commit 34601dc

File tree

5 files changed

+87
-21
lines changed

5 files changed

+87
-21
lines changed

src/client/Microsoft.Identity.Client/ManagedIdentity/AbstractManagedIdentity.cs

+40-15
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
using System.Net;
1313
using Microsoft.Identity.Client.ApiConfig.Parameters;
1414
using System.Text;
15+
#if SUPPORTS_SYSTEM_TEXT_JSON
16+
using System.Text.Json;
17+
#else
18+
using Microsoft.Identity.Json;
19+
#endif
1520

1621
namespace Microsoft.Identity.Client.ManagedIdentity
1722
{
@@ -29,7 +34,7 @@ protected AbstractManagedIdentity(RequestContext requestContext, ManagedIdentity
2934
}
3035

3136
public virtual async Task<ManagedIdentityResponse> AuthenticateAsync(
32-
AcquireTokenForManagedIdentityParameters parameters,
37+
AcquireTokenForManagedIdentityParameters parameters,
3338
CancellationToken cancellationToken)
3439
{
3540
if (cancellationToken.IsCancellationRequested)
@@ -107,7 +112,7 @@ protected virtual Task<ManagedIdentityResponse> HandleResponseAsync(
107112
}
108113

109114
string message = GetMessageFromErrorResponse(response);
110-
115+
111116
_requestContext.Logger.Error($"[Managed Identity] request failed, HttpStatusCode: {response.StatusCode} Error message: {message}");
112117

113118
MsalException exception = MsalServiceExceptionFactory.CreateManagedIdentityException(
@@ -124,20 +129,39 @@ protected virtual Task<ManagedIdentityResponse> HandleResponseAsync(
124129

125130
protected ManagedIdentityResponse GetSuccessfulResponse(HttpResponse response)
126131
{
127-
ManagedIdentityResponse managedIdentityResponse = JsonHelper.DeserializeFromJson<ManagedIdentityResponse>(response.Body);
132+
ManagedIdentityResponse managedIdentityResponse;
133+
try
134+
{
135+
managedIdentityResponse = JsonHelper.DeserializeFromJson<ManagedIdentityResponse>(response.Body);
136+
}
137+
catch (JsonException ex)
138+
{
139+
_requestContext.Logger.Error("[Managed Identity] MSI json response failed to parse. " + ex);
128140

129-
if (managedIdentityResponse == null || managedIdentityResponse.AccessToken.IsNullOrEmpty() || managedIdentityResponse.ExpiresOn.IsNullOrEmpty())
141+
var exception = MsalServiceExceptionFactory.CreateManagedIdentityException(
142+
MsalError.ManagedIdentityResponseParseFailure,
143+
MsalErrorMessage.ManagedIdentityJsonParseFailure,
144+
ex,
145+
_sourceType,
146+
(int)HttpStatusCode.OK);
147+
148+
throw exception;
149+
}
150+
151+
if (managedIdentityResponse == null ||
152+
managedIdentityResponse.AccessToken.IsNullOrEmpty() ||
153+
managedIdentityResponse.ExpiresOn.IsNullOrEmpty())
130154
{
131155
_requestContext.Logger.Error("[Managed Identity] Response is either null or insufficient for authentication.");
132156

133157
var exception = MsalServiceExceptionFactory.CreateManagedIdentityException(
134158
MsalError.ManagedIdentityRequestFailed,
135159
MsalErrorMessage.ManagedIdentityInvalidResponse,
136-
null,
137-
_sourceType,
138-
null);
160+
null,
161+
_sourceType,
162+
(int)HttpStatusCode.OK);
139163

140-
throw exception;
164+
throw exception;
141165
}
142166

143167
return managedIdentityResponse;
@@ -158,7 +182,7 @@ internal string GetMessageFromErrorResponse(HttpResponse response)
158182
catch
159183
{
160184
return TryGetMessageFromNestedErrorResponse(response.Body);
161-
}
185+
}
162186
}
163187

164188
private string ExtractErrorMessageFromManagedIdentityErrorResponse(ManagedIdentityErrorResponse managedIdentityErrorResponse)
@@ -218,7 +242,8 @@ private string TryGetMessageFromNestedErrorResponse(string response)
218242
{
219243
return errorMessage.ToString();
220244
}
221-
} catch
245+
}
246+
catch
222247
{
223248
// Ignore any exceptions that occur during parsing and send the error message.
224249
}
@@ -227,8 +252,8 @@ private string TryGetMessageFromNestedErrorResponse(string response)
227252
return $"{MsalErrorMessage.ManagedIdentityUnexpectedErrorResponse}. Error response received from the server: {response}.";
228253
}
229254

230-
private void HandleException(Exception ex,
231-
ManagedIdentitySource managedIdentitySource = ManagedIdentitySource.None,
255+
private void HandleException(Exception ex,
256+
ManagedIdentitySource managedIdentitySource = ManagedIdentitySource.None,
232257
string additionalInfo = null)
233258
{
234259
ManagedIdentitySource source = managedIdentitySource != ManagedIdentitySource.None ? managedIdentitySource : _sourceType;
@@ -254,9 +279,9 @@ private void HandleException(Exception ex,
254279
}
255280
}
256281

257-
private static void CreateAndThrowException(string errorCode,
258-
string errorMessage,
259-
Exception innerException,
282+
private static void CreateAndThrowException(string errorCode,
283+
string errorMessage,
284+
Exception innerException,
260285
ManagedIdentitySource source)
261286
{
262287
MsalException exception = MsalServiceExceptionFactory.CreateManagedIdentityException(

src/client/Microsoft.Identity.Client/MsalError.cs

+5
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,11 @@ public static class MsalError
11011101
/// </summary>
11021102
public const string ManagedIdentityRequestFailed = "managed_identity_request_failed";
11031103

1104+
/// <summary>
1105+
/// Managed Identity error response was received.
1106+
/// </summary>
1107+
public const string ManagedIdentityResponseParseFailure = "managed_identity_response_parse_failure";
1108+
11041109
/// <summary>
11051110
/// Managed Identity endpoint is not reachable.
11061111
/// </summary>

src/client/Microsoft.Identity.Client/MsalErrorMessage.cs

+1
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@ public static string InvalidTokenProviderResponseValue(string invalidValueName)
412412

413413
public const string ManagedIdentityNoResponseReceived = "[Managed Identity] Authentication unavailable. No response received from the managed identity endpoint.";
414414
public const string ManagedIdentityInvalidResponse = "[Managed Identity] Invalid response, the authentication response received did not contain the expected fields.";
415+
public const string ManagedIdentityJsonParseFailure = "[Managed Identity] MSI returned 200 OK, but the response could not be parsed.";
415416
public const string ManagedIdentityUnexpectedResponse = "[Managed Identity] Unexpected exception occurred when parsing the response. See the inner exception for details.";
416417
public const string ManagedIdentityExactlyOneScopeExpected = "[Managed Identity] To acquire token for managed identity, exactly one scope must be passed.";
417418
public const string ManagedIdentityUnexpectedErrorResponse = "[Managed Identity] The error response was either empty or could not be parsed.";

tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHelpers.cs

+3-6
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,10 @@ public static string GetMsiSuccessfulResponse(int expiresInHours = 1, bool useIs
133133
"\"Bearer\",\"client_id\":\"client_id\"}";
134134
}
135135

136-
public static string GetMsiImdsSuccessfulResponse()
136+
public static string GetMsiErrorBadJson()
137137
{
138-
string expiresOn = DateTimeHelpers.DateTimeToUnixTimestamp(DateTime.UtcNow.AddHours(1));
139-
return
140-
"{\"access_token\":\"" + TestConstants.ATSecret + "\",\"client_id\":\"client-id\"," +
141-
"\"expires_in\":\"12345\",\"expires_on\":\"" + expiresOn + "\",\"resource\":\"https://management.azure.com/\"," +
142-
"\"ext_expires_in\":\"12345\",\"token_type\":\"Bearer\"}";
138+
string successResponse = GetMsiSuccessfulResponse();
139+
return successResponse.Replace("{", "|");
143140
}
144141

145142
public static string GetMsiErrorResponse(ManagedIdentitySource source)

tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/ManagedIdentityTests.cs

+38
Original file line numberDiff line numberDiff line change
@@ -1073,5 +1073,43 @@ await AssertException.TaskThrowsAsync<TaskCanceledException>(
10731073
.WithForceRefresh(true)
10741074
.ExecuteAsync(tokenSource.Token)).ConfigureAwait(false);
10751075
}
1076+
1077+
[DataTestMethod]
1078+
[DataRow(ManagedIdentitySource.Imds, ImdsEndpoint)]
1079+
[DataRow(ManagedIdentitySource.AppService, AppServiceEndpoint)]
1080+
[DataRow(ManagedIdentitySource.AzureArc, AzureArcEndpoint)]
1081+
[DataRow(ManagedIdentitySource.CloudShell, CloudShellEndpoint)]
1082+
[DataRow(ManagedIdentitySource.ServiceFabric, ServiceFabricEndpoint)]
1083+
public async Task InvalidJsonResponseHandling(ManagedIdentitySource managedIdentitySource, string endpoint)
1084+
{
1085+
using (new EnvVariableContext())
1086+
using (var httpManager = new MockHttpManager(isManagedIdentity: true))
1087+
{
1088+
SetEnvironmentVariables(managedIdentitySource, endpoint);
1089+
1090+
var miBuilder = ManagedIdentityApplicationBuilder
1091+
.Create(ManagedIdentityId.SystemAssigned)
1092+
.WithHttpManager(httpManager);
1093+
1094+
// Disabling shared cache options to avoid cross test pollution.
1095+
miBuilder.Config.AccessorOptions = null;
1096+
1097+
var mi = miBuilder.Build();
1098+
1099+
httpManager.AddManagedIdentityMockHandler(
1100+
endpoint,
1101+
"scope",
1102+
MockHelpers.GetMsiErrorBadJson(),
1103+
managedIdentitySource);
1104+
1105+
MsalServiceException ex = await Assert.ThrowsExceptionAsync<MsalServiceException>(async () =>
1106+
await mi.AcquireTokenForManagedIdentity("scope")
1107+
.ExecuteAsync().ConfigureAwait(false)).ConfigureAwait(false);
1108+
1109+
Assert.AreEqual(managedIdentitySource.ToString(), ex.AdditionalExceptionData[MsalException.ManagedIdentitySource]);
1110+
Assert.AreEqual(MsalError.ManagedIdentityResponseParseFailure, ex.ErrorCode);
1111+
Assert.AreEqual(MsalErrorMessage.ManagedIdentityJsonParseFailure, ex.Message);
1112+
}
1113+
}
10761114
}
10771115
}

0 commit comments

Comments
 (0)