Skip to content

Commit edd4114

Browse files
committed
# Conflicts: # msal4j-sdk/src/test/java/com/microsoft/aad/msal4j/TestHelper.java
2 parents 538e1a5 + 5259675 commit edd4114

File tree

6 files changed

+132
-10
lines changed

6 files changed

+132
-10
lines changed

msal4j-brokers/pom.xml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
<dependency>
4545
<groupId>org.projectlombok</groupId>
4646
<artifactId>lombok</artifactId>
47-
<version>1.18.6</version>
47+
<version>1.18.36</version>
4848
<scope>provided</scope>
4949
</dependency>
5050
<dependency>
@@ -126,7 +126,14 @@
126126
<plugin>
127127
<groupId>org.projectlombok</groupId>
128128
<artifactId>lombok-maven-plugin</artifactId>
129-
<version>1.18.2.0</version>
129+
<version>1.18.20.0</version>
130+
<dependencies>
131+
<dependency>
132+
<groupId>org.projectlombok</groupId>
133+
<artifactId>lombok</artifactId>
134+
<version>1.18.36</version>
135+
</dependency>
136+
</dependencies>
130137
<executions>
131138
<execution>
132139
<goals>

msal4j-sdk/pom.xml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
<dependency>
5858
<groupId>org.projectlombok</groupId>
5959
<artifactId>lombok</artifactId>
60-
<version>1.18.6</version>
60+
<version>1.18.36</version>
6161
<scope>provided</scope>
6262
</dependency>
6363
<dependency>
@@ -207,7 +207,14 @@
207207
<plugin>
208208
<groupId>org.projectlombok</groupId>
209209
<artifactId>lombok-maven-plugin</artifactId>
210-
<version>1.18.2.0</version>
210+
<version>1.18.20.0</version>
211+
<dependencies>
212+
<dependency>
213+
<groupId>org.projectlombok</groupId>
214+
<artifactId>lombok</artifactId>
215+
<version>1.18.36</version>
216+
</dependency>
217+
</dependencies>
211218
<executions>
212219
<execution>
213220
<goals>
@@ -323,7 +330,7 @@
323330
<plugin>
324331
<groupId>biz.aQute.bnd</groupId>
325332
<artifactId>bnd-maven-plugin</artifactId>
326-
<version>4.3.1</version>
333+
<version>5.2.0</version>
327334
<executions>
328335
<execution>
329336
<goals>

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,8 @@ private Optional<AccessTokenCacheEntity> getAccessTokenCacheEntity(
477477

478478
return accessTokens.values().stream().filter(
479479
accessToken ->
480-
accessToken.homeAccountId.equals(account.homeAccountId()) &&
480+
accessToken.homeAccountId != null &&
481+
accessToken.homeAccountId.equals(account.homeAccountId()) &&
481482
environmentAliases.contains(accessToken.environment) &&
482483
accessToken.realm.equals(authority.tenant()) &&
483484
accessToken.clientId.equals(clientId) &&
@@ -552,6 +553,7 @@ private Optional<RefreshTokenCacheEntity> getRefreshTokenCacheEntity(
552553

553554
return refreshTokens.values().stream().filter(
554555
refreshToken ->
556+
refreshToken.homeAccountId != null &&
555557
refreshToken.homeAccountId.equals(account.homeAccountId()) &&
556558
environmentAliases.contains(refreshToken.environment) &&
557559
refreshToken.clientId.equals(clientId)

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import java.io.IOException;
1818
import java.net.MalformedURLException;
19+
import java.nio.charset.StandardCharsets;
1920
import java.util.*;
2021

2122
@Getter(AccessLevel.PACKAGE)
@@ -118,9 +119,15 @@ private AuthenticationResult createAuthenticationResultFromOauthHttpResponse(
118119
}
119120

120121
AccountCacheEntity accountCacheEntity = null;
122+
if (!StringHelper.isNullOrBlank(tokens.getIDTokenString())) {
123+
String idTokenJson;
124+
try {
125+
idTokenJson = new String(Base64.getDecoder().decode(tokens.getIDTokenString().split("\\.")[1]), StandardCharsets.UTF_8);
126+
} catch (ArrayIndexOutOfBoundsException e) {
127+
throw new MsalServiceException("Error parsing ID token, missing payload section. Ensure that the ID token is following the JWT format.",
128+
AuthenticationErrorCode.INVALID_JWT);
129+
}
121130

122-
if (tokens.getIDToken() != null) {
123-
String idTokenJson = tokens.getIDToken().getParsedParts()[1].decodeToString();
124131
IdToken idToken = JsonHelper.convertJsonToObject(idTokenJson, IdToken.class);
125132

126133
AuthorityType type = msalRequest.application().authenticationAuthority.authorityType;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.aad.msal4j;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
import java.util.Collections;
9+
import java.util.HashMap;
10+
11+
import static org.junit.jupiter.api.Assertions.assertEquals;
12+
import static org.junit.jupiter.api.Assertions.assertNull;
13+
import static org.mockito.ArgumentMatchers.any;
14+
import static org.mockito.Mockito.mock;
15+
import static org.mockito.Mockito.times;
16+
import static org.mockito.Mockito.verify;
17+
import static org.mockito.Mockito.when;
18+
19+
class CacheTests {
20+
21+
@Test
22+
void cacheLookup_MixAccountBasedAndAssertionBasedSilentFlows() throws Exception {
23+
DefaultHttpClient httpClientMock = mock(DefaultHttpClient.class);
24+
25+
ConfidentialClientApplication cca =
26+
ConfidentialClientApplication.builder("clientId", ClientCredentialFactory.createFromSecret("password"))
27+
.authority("https://login.microsoftonline.com/tenant/")
28+
.instanceDiscovery(false)
29+
.validateAuthority(false)
30+
.httpClient(httpClientMock)
31+
.build();
32+
33+
HashMap<String, String> responseParameters = new HashMap<>();
34+
//Acquire a token with no ID token/account associated with it
35+
responseParameters.put("access_token", "accessTokenNoAccount");
36+
37+
ClientCredentialParameters clientCredentialParameters = ClientCredentialParameters.builder(Collections.singleton("someScopes")).build();
38+
when(httpClientMock.send(any(HttpRequest.class))).thenReturn(TestHelper.expectedResponse(200, TestHelper.getSuccessfulTokenResponse(responseParameters)));
39+
IAuthenticationResult resultNoAccount = cca.acquireToken(clientCredentialParameters).get();
40+
41+
//Ensure there is one token in the cache, and the result had no account
42+
assertEquals(1, cca.tokenCache.accessTokens.size());
43+
assertNull(resultNoAccount.account());
44+
verify(httpClientMock, times(1)).send(any());
45+
46+
//Acquire a second token, this time with an ID token/account
47+
responseParameters.put("access_token", "accessTokenWithAccount");
48+
responseParameters.put("id_token", TestHelper.createIdToken(new HashMap<>()));
49+
50+
when(httpClientMock.send(any(HttpRequest.class))).thenReturn(TestHelper.expectedResponse(200, TestHelper.getSuccessfulTokenResponse(responseParameters)));
51+
OnBehalfOfParameters onBehalfOfParametersarameters = OnBehalfOfParameters.builder(Collections.singleton("someOtherScopes"), new UserAssertion(TestHelper.signedAssertion)).build();
52+
IAuthenticationResult resultWithAccount = cca.acquireToken(onBehalfOfParametersarameters).get();
53+
54+
//Ensure there are now two tokens in the cache, and the result has an account
55+
assertEquals(2, cca.tokenCache.accessTokens.size());
56+
assertNull(resultNoAccount.account());
57+
verify(httpClientMock, times(2)).send(any());
58+
59+
//Make two silent calls, one with the account and one without
60+
SilentParameters silentParametersNoAccount = SilentParameters.builder(Collections.singleton("someScopes")).build();
61+
SilentParameters silentParametersWithAccount = SilentParameters.builder(Collections.singleton("someOtherScopes"), resultWithAccount.account()).build();
62+
63+
resultNoAccount = cca.acquireTokenSilently(silentParametersNoAccount).get();
64+
resultWithAccount = cca.acquireTokenSilently(silentParametersWithAccount).get();
65+
66+
//Ensure the correct access tokens were returned from each silent call
67+
assertEquals("accessTokenNoAccount", resultNoAccount.accessToken());
68+
assertEquals("accessTokenWithAccount", resultWithAccount.accessToken());
69+
}
70+
}

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

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.security.*;
2020
import java.security.cert.CertificateException;
2121
import java.security.cert.X509Certificate;
22+
import java.util.Base64;
2223
import java.util.Collections;
2324
import java.util.HashMap;
2425
import java.util.List;
@@ -34,6 +35,17 @@ class TestHelper {
3435
"\"expires_on\": %d ,\"expires_in\": %d," +
3536
"\"token_type\":\"Bearer\"}";
3637

38+
static final String idTokenFormat = "{\"aud\": \"%s\"," +
39+
"\"iss\": \"%s\"," +
40+
"\"iat\": 1455833828," + "\"nbf\": 1455833828," + "\"exp\": 1455837728," +
41+
"\"ipaddr\": \"131.107.159.117\"," +
42+
"\"name\": \"%s\"," +
43+
"\"oid\": \"%s\"," +
44+
"\"preferred_username\": \"%s\"," +
45+
"\"sub\": \"%s\"," +
46+
"\"tid\": \"%s\"," +
47+
"\"ver\": \"2.0\"}";
48+
3749
static X509Certificate x509Cert = getX509Cert();
3850
static PrivateKey privateKey = getPrivateKey();
3951

@@ -84,10 +96,10 @@ static String getSuccessfulTokenResponse(HashMap<String, String> responseValues)
8496

8597
return String.format(successfulResponseFormat,
8698
responseValues.getOrDefault("access_token", "access_token"),
87-
responseValues.getOrDefault("id_token", "id_token"),
99+
responseValues.getOrDefault("id_token", ""),
88100
responseValues.getOrDefault("refresh_token", "refresh_token"),
89101
responseValues.getOrDefault("client_id", "client_id"),
90-
responseValues.getOrDefault("client_info", "client_info"),
102+
responseValues.getOrDefault("client_info", "eyJ1aWQiOiI1OTdmODZjZC0xM2YzLTQ0YzAtYmVjZS1hMWU3N2JhNDMyMjgiLCJ1dGlkIjoiZjY0NWFkOTItZTM4ZC00ZDFhLWI1MTAtZDFiMDlhNzRhOGNhIn0"),
91103
expiresOn,
92104
expiresIn
93105
);
@@ -106,6 +118,23 @@ static HttpResponse expectedResponse(int statusCode, String response) {
106118
return httpResponse;
107119
}
108120

121+
//Maps various values to the idTokenFormat string
122+
static String createIdToken(HashMap<String, String> idTokenValues) {
123+
String tokenValues = String.format(idTokenFormat,
124+
idTokenValues.getOrDefault("aud", "e854a4a7-6c34-449c-b237-fc7a28093d84"),
125+
idTokenValues.getOrDefault("iss", "https://login.microsoftonline.com/6c3d51dd-f0e5-4959-b4ea-a80c4e36fe5e/v2.0/"),
126+
idTokenValues.getOrDefault("name", "name"),
127+
idTokenValues.getOrDefault("oid", "oid"),
128+
idTokenValues.getOrDefault("preferred_username", "preferred_username"),
129+
idTokenValues.getOrDefault("sub", "K4_SGGxKqW1SxUAmhg6C1F6VPiFzcx-Qd80ehIEdFus"),
130+
idTokenValues.getOrDefault("client_info", "eyJ1aWQiOiI1OTdmODZjZC0xM2YzLTQ0YzAtYmVjZS1hMWU3N2JhNDMyMjgiLCJ1dGlkIjoiZjY0NWFkOTItZTM4ZC00ZDFhLWI1MTAtZDFiMDlhNzRhOGNhIn0")
131+
);
132+
133+
String encodedTokenValues = Base64.getUrlEncoder().encodeToString(tokenValues.getBytes());
134+
135+
return String.format("someheader.%s.somesignature", encodedTokenValues);
136+
}
137+
109138
static void setPrivateKeyAndCert() {
110139
try {
111140
CertAndKeyGen certGen = new CertAndKeyGen("RSA", "SHA256WithRSA", null);

0 commit comments

Comments
 (0)