Skip to content

Commit 11afa2e

Browse files
committed
Add a distinct MsalJsonParsingException for JSON parsing failures
1 parent 3699125 commit 11afa2e

File tree

6 files changed

+95
-9
lines changed

6 files changed

+95
-9
lines changed

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AbstractManagedIdentitySource.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,12 @@ public ManagedIdentityResponse handleResponse(
9393

9494
protected ManagedIdentityResponse getSuccessfulResponse(IHttpResponse response) {
9595

96-
ManagedIdentityResponse managedIdentityResponse = JsonHelper.convertJsonStringToJsonSerializableObject(response.body(), ManagedIdentityResponse::fromJson);
96+
ManagedIdentityResponse managedIdentityResponse;
97+
try {
98+
managedIdentityResponse = JsonHelper.convertJsonStringToJsonSerializableObject(response.body(), ManagedIdentityResponse::fromJson);
99+
} catch (MsalJsonParsingException e) {
100+
throw new MsalJsonParsingException(String.format(MsalErrorMessage.MANAGED_IDENTITY_RESPONSE_SUCCESSFUL_PARSE_FAILURE, e.getMessage()), MsalError.MANAGED_IDENTITY_RESPONSE_PARSE_FAILURE, managedIdentitySourceType);
101+
}
97102

98103
if (managedIdentityResponse == null || managedIdentityResponse.getAccessToken() == null
99104
|| managedIdentityResponse.getAccessToken().isEmpty() || managedIdentityResponse.getExpiresOn() == null
@@ -105,8 +110,13 @@ protected ManagedIdentityResponse getSuccessfulResponse(IHttpResponse response)
105110
}
106111

107112
protected String getMessageFromErrorResponse(IHttpResponse response) {
108-
ManagedIdentityErrorResponse managedIdentityErrorResponse =
109-
JsonHelper.convertJsonToObject(response.body(), ManagedIdentityErrorResponse.class);
113+
114+
ManagedIdentityErrorResponse managedIdentityErrorResponse;
115+
try {
116+
managedIdentityErrorResponse = JsonHelper.convertJsonToObject(response.body(), ManagedIdentityErrorResponse.class);
117+
} catch (MsalJsonParsingException e) {
118+
throw new MsalJsonParsingException(String.format(MsalErrorMessage.MANAGED_IDENTITY_RESPONSE_FAILED_PARSE_FAILURE, e.getMessage()), MsalError.MANAGED_IDENTITY_RESPONSE_PARSE_FAILURE, managedIdentitySourceType);
119+
}
110120

111121
if (managedIdentityErrorResponse == null) {
112122
return MANAGED_IDENTITY_NO_RESPONSE_RECEIVED;

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/JsonHelper.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,17 @@ private JsonHelper() {
3535
static <T> T convertJsonToObject(final String json, final Class<T> tClass) {
3636
try {
3737
return mapper.readValue(json, tClass);
38-
} catch (IOException e) {
39-
throw new MsalClientException(e);
38+
} catch (Exception e) {
39+
throw new MsalJsonParsingException(e.getMessage(), AuthenticationErrorCode.INVALID_JSON);
4040
}
4141
}
4242

4343
//This method is used to convert a JSON string to an object which implements the JsonSerializable interface from com.azure.json
4444
static <T extends JsonSerializable<T>> T convertJsonStringToJsonSerializableObject(String jsonResponse, ReadValueCallback<JsonReader, T> readFunction) {
4545
try (JsonReader jsonReader = JsonProviders.createReader(jsonResponse)) {
4646
return readFunction.read(jsonReader);
47-
} catch (IOException e) {
48-
throw new MsalClientException(e.getMessage(), AuthenticationErrorCode.INVALID_JSON);
4947
} catch (Exception e) {
50-
throw new MsalClientException("Error parsing JSON response: " + e.getMessage(), AuthenticationErrorCode.INVALID_JSON);
48+
throw new MsalJsonParsingException(e.getMessage(), AuthenticationErrorCode.INVALID_JSON);
5149
}
5250
}
5351

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/MsalError.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,6 @@ public class MsalError {
3434
public static final String MANAGED_IDENTITY_UNREACHABLE_NETWORK = "managed_identity_unreachable_network";
3535

3636
public static final String MANAGED_IDENTITY_FILE_READ_ERROR = "managed_identity_file_read_error";
37+
38+
public static final String MANAGED_IDENTITY_RESPONSE_PARSE_FAILURE = "managed_identity_response_parse_failure";
3739
}

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/MsalErrorMessage.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,8 @@ class MsalErrorMessage {
2626
public static final String IDENTITY_UNAVAILABLE_ERROR = "[Managed Identity] Authentication unavailable. The requested identity has not been assigned to this resource.";
2727

2828
public static final String GATEWAY_ERROR = "[Managed Identity] Authentication unavailable. The request failed due to a gateway error.";
29+
30+
public static final String MANAGED_IDENTITY_RESPONSE_SUCCESSFUL_PARSE_FAILURE = "[Managed Identity] MSI returned 200 OK, but the response could not be parsed: %s";
31+
32+
public static final String MANAGED_IDENTITY_RESPONSE_FAILED_PARSE_FAILURE = "[Managed Identity] MSI did not return 200 OK, and the response could not be parsed: %s";
2933
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.aad.msal4j;
5+
6+
/**
7+
* This exception class informs developers that a response contained invalid JSON or otherwise could not be parsed
8+
*/
9+
public class MsalJsonParsingException extends MsalServiceException {
10+
11+
/**
12+
* Initializes a new instance of the exception class with a specified error message
13+
*
14+
* @param message the error message that explains the reason for the exception
15+
* @param error a simplified error code from {@link AuthenticationErrorCode} and used for references in documentation
16+
*/
17+
MsalJsonParsingException(final String message, final String error) {
18+
super(message, error);
19+
}
20+
21+
/**
22+
* Initializes a new instance of the exception class, with extra properties for a Managed Identity error
23+
*
24+
* @param message the error message that explains the reason for the exception
25+
* @param error a simplified error code
26+
* @param managedIdentitySource the Managed Identity service
27+
*/
28+
MsalJsonParsingException(
29+
final String message, final String error,
30+
ManagedIdentitySourceType managedIdentitySource) {
31+
super(message, error, managedIdentitySource);
32+
}
33+
34+
}

msal4j-sdk/src/test/java/com/microsoft/aad/msal4j/ManagedIdentityTests.java

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ private String getSuccessfulResponse(String resource) {
4949
"\"Bearer\",\"client_id\":\"client_id\"}";
5050
}
5151

52+
private String getSuccessfulResponseWithInvalidJson() {
53+
return "missing starting bracket \"access_token\":\"accesstoken\",\"token_type\":" + "\"Bearer\",\"client_id\":\"a bunch of problems}";
54+
}
55+
5256
private String getMsiErrorResponse() {
5357
return "{\"statusCode\":\"500\",\"message\":\"An unexpected error occured while fetching the AAD Token.\",\"correlationId\":\"7d0c9763-ff1d-4842-a3f3-6d49e64f4513\"}";
5458
}
@@ -203,6 +207,40 @@ void managedIdentityTest_SystemAssigned_SuccessfulResponse(ManagedIdentitySource
203207
verify(httpClientMock, times(1)).send(any());
204208
}
205209

210+
@ParameterizedTest
211+
@MethodSource("com.microsoft.aad.msal4j.ManagedIdentityTestDataProvider#createData")
212+
void managedIdentityTest_SuccessfulResponse_WithInvalidJson(ManagedIdentitySourceType source, String endpoint, String resource) throws Exception {
213+
IEnvironmentVariables environmentVariables = new EnvironmentVariablesHelper(source, endpoint);
214+
ManagedIdentityApplication.setEnvironmentVariables(environmentVariables);
215+
DefaultHttpClient httpClientMock = mock(DefaultHttpClient.class);
216+
if (source == SERVICE_FABRIC) {
217+
ServiceFabricManagedIdentitySource.setHttpClient(httpClientMock);
218+
}
219+
220+
when(httpClientMock.send(expectedRequest(source, resource))).thenReturn(expectedResponse(200, getSuccessfulResponseWithInvalidJson()));
221+
222+
miApp = ManagedIdentityApplication
223+
.builder(ManagedIdentityId.systemAssigned())
224+
.httpClient(httpClientMock)
225+
.build();
226+
227+
// Clear caching to avoid cross test pollution.
228+
miApp.tokenCache().accessTokens.clear();
229+
230+
try {
231+
miApp.acquireTokenForManagedIdentity(
232+
ManagedIdentityParameters.builder(resource)
233+
.build()).get();
234+
fail("MsalServiceException is expected but not thrown.");
235+
} catch (ExecutionException exception) {
236+
assert(exception.getCause() instanceof MsalJsonParsingException);
237+
238+
MsalJsonParsingException miException = (MsalJsonParsingException) exception.getCause();
239+
assertEquals(source.name(), miException.managedIdentitySource());
240+
assertEquals(MANAGED_IDENTITY_RESPONSE_PARSE_FAILURE, miException.errorCode());
241+
}
242+
}
243+
206244
@ParameterizedTest
207245
@MethodSource("com.microsoft.aad.msal4j.ManagedIdentityTestDataProvider#createDataUserAssigned")
208246
void managedIdentityTest_UserAssigned_SuccessfulResponse(ManagedIdentitySourceType source, String endpoint, ManagedIdentityId id) throws Exception {
@@ -454,7 +492,7 @@ void managedIdentity_RequestFailed_NoPayload(ManagedIdentitySourceType source, S
454492

455493
MsalServiceException miException = (MsalServiceException) exception.getCause();
456494
assertEquals(source.name(), miException.managedIdentitySource());
457-
assertEquals(AuthenticationErrorCode.MANAGED_IDENTITY_REQUEST_FAILED, miException.errorCode());
495+
assertEquals(MANAGED_IDENTITY_RESPONSE_PARSE_FAILURE, miException.errorCode());
458496
return;
459497
}
460498

0 commit comments

Comments
 (0)