Skip to content

Commit 0e775fc

Browse files
committed
Use Base64URL encoding and add test
1 parent d846a76 commit 0e775fc

File tree

2 files changed

+43
-1
lines changed

2 files changed

+43
-1
lines changed

Diff for: msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/TokenRequestExecutor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ private AuthenticationResult createAuthenticationResultFromOauthHttpResponse(
119119
if (!StringHelper.isNullOrBlank(tokens.getIDTokenString())) {
120120
String idTokenJson;
121121
try {
122-
idTokenJson = new String(Base64.getDecoder().decode(tokens.getIDTokenString().split("\\.")[1]), StandardCharsets.UTF_8);
122+
idTokenJson = new String(Base64.getUrlDecoder().decode(tokens.getIDTokenString().split("\\.")[1]), StandardCharsets.UTF_8);
123123
} catch (ArrayIndexOutOfBoundsException e) {
124124
throw new MsalServiceException("Error parsing ID token, missing payload section. Ensure that the ID token is following the JWT format.",
125125
AuthenticationErrorCode.INVALID_JWT);

Diff for: msal4j-sdk/src/test/java/com/microsoft/aad/msal4j/TokenRequestExecutorTest.java

+42
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@
2727
import java.net.URI;
2828
import java.net.URISyntaxException;
2929
import java.net.URL;
30+
import java.util.Base64;
3031
import java.util.Collections;
3132
import java.util.HashMap;
33+
import java.util.concurrent.ExecutionException;
3234

3335
@ExtendWith(MockitoExtension.class)
3436
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@@ -294,4 +296,44 @@ void testExecuteOAuth_Failure() throws SerializeException,
294296

295297
assertThrows(MsalException.class, request::executeTokenRequest);
296298
}
299+
300+
@Test
301+
void testBase64UrlEncoding() throws MalformedURLException, ExecutionException, InterruptedException {
302+
DefaultHttpClient httpClientMock = mock(DefaultHttpClient.class);
303+
ConfidentialClientApplication cca =
304+
ConfidentialClientApplication.builder("clientId", ClientCredentialFactory.createFromSecret("password"))
305+
.authority("https://login.microsoftonline.com/tenant/")
306+
.instanceDiscovery(false)
307+
.validateAuthority(false)
308+
.httpClient(httpClientMock)
309+
.build();
310+
311+
//ID token payloads are parsed to get certain info to create Account and AccountCacheEntity objects, and the library must decode them using a Base64URL decoder.
312+
HashMap<String, String> tokenParameters = new HashMap<>();
313+
tokenParameters.put("preferred_username", "~nameWith~specialChars");
314+
String encodedIDToken = TestHelper.createIdToken(tokenParameters);
315+
try {
316+
//TestHelper.createIdToken() should use Base64URL encoding, so first we prove that the encoded token it produces cannot be decoded with Base64 decoder
317+
Base64.getDecoder().decode(encodedIDToken.split("\\.")[1]);
318+
319+
fail("IllegalArgumentException was expected but not thrown.");
320+
} catch (IllegalArgumentException e) {
321+
//Encoded token should have some "-" characters in it
322+
assertTrue(e.getMessage().contains("Illegal base64 character 2d"));
323+
}
324+
325+
//Now, send that encoded token through the library's token request flow, which will decode it using a Base64URL decoder
326+
HashMap<String, String> responseParameters = new HashMap<>();
327+
responseParameters.put("id_token", encodedIDToken);
328+
responseParameters.put("access_token", "token");
329+
TestHelper.createTokenRequestMock(httpClientMock, TestHelper.getSuccessfulTokenResponse(responseParameters), 200);
330+
331+
OnBehalfOfParameters parameters = OnBehalfOfParameters.builder(Collections.singleton("someScopes"), new UserAssertion(TestHelper.signedAssertion)).build();
332+
IAuthenticationResult result = cca.acquireToken(parameters).get();
333+
334+
//Ensure that the name was successfully parsed out of the encoded ID token
335+
assertNotNull(result.idToken());
336+
assertNotNull(result.account());
337+
assertEquals("~nameWith~specialChars", result.account().username());
338+
}
297339
}

0 commit comments

Comments
 (0)