Skip to content

Commit bdac7ec

Browse files
authored
Merge pull request #933 from AzureAD/avdunn/mi-exceptions
Add a distinct MsalJsonParsingException for JSON parsing failures
2 parents 20ed99e + c9312d9 commit bdac7ec

File tree

7 files changed

+95
-10
lines changed

7 files changed

+95
-10
lines changed

msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ void DeviceCodeFlowADFSv2019Test() throws Exception {
7676
IntegrationTestHelper.assertAccessAndIdTokensNotNull(result);
7777
}
7878

79-
@Test()
79+
//TODO: This test is failing intermittently in the pipeline runs for the same commit, but always passes locally. Disabling until we can investigate more.
80+
//@Test()
8081
void DeviceCodeFlowMSATest() throws Exception {
8182

8283
User user = labUserProvider.getMSAUser();

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_PARSE_FAILURE, response.statusCode(), 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_PARSE_FAILURE, response.statusCode(), 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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,6 @@ 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_PARSE_FAILURE = "[Managed Identity] MSI returned %s, but the response could not be parsed: %s";
2931
}
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
}
@@ -202,6 +206,40 @@ void managedIdentityTest_SystemAssigned_SuccessfulResponse(ManagedIdentitySource
202206
verify(httpClientMock, times(1)).send(any());
203207
}
204208

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

457495
MsalServiceException miException = (MsalServiceException) exception.getCause();
458496
assertEquals(source.name(), miException.managedIdentitySource());
459-
assertEquals(AuthenticationErrorCode.MANAGED_IDENTITY_REQUEST_FAILED, miException.errorCode());
497+
assertEquals(MsalError.MANAGED_IDENTITY_RESPONSE_PARSE_FAILURE, miException.errorCode());
460498
return;
461499
}
462500

0 commit comments

Comments
 (0)