Skip to content

Commit 962405a

Browse files
authored
Merge pull request #928 from AzureAD/avdunn/nimbus-tokens
Remove usage of com.nimbusds.oauth2's Token classes
2 parents c9d4237 + be2debc commit 962405a

File tree

11 files changed

+105
-175
lines changed

11 files changed

+105
-175
lines changed

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

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33

44
package com.microsoft.aad.msal4j;
55

6-
import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
7-
import net.minidev.json.JSONObject;
8-
import com.nimbusds.oauth2.sdk.ParseException;
6+
import com.azure.json.JsonProviders;
7+
import com.azure.json.JsonReader;
98

9+
import java.io.IOException;
1010
import java.util.Arrays;
1111
import java.util.HashMap;
1212
import java.util.List;
@@ -62,12 +62,8 @@ List<String> getHeader(String key) {
6262
return headers.get(key);
6363
}
6464

65-
JSONObject getBodyAsJson() {
66-
try {
67-
return JSONObjectUtils.parse(this.body());
68-
} catch (ParseException e) {
69-
throw new RuntimeException(e);
70-
}
65+
Map<String, String> getBodyAsMap() {
66+
return JsonHelper.convertJsonToMap(this.body);
7167
}
7268

7369
public int statusCode() {

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.io.IOException;
1818
import java.util.ArrayList;
1919
import java.util.Iterator;
20+
import java.util.Map;
2021
import java.util.Set;
2122

2223
class JsonHelper {
@@ -51,6 +52,16 @@ static <T extends JsonSerializable<T>> T convertJsonStringToJsonSerializableObje
5152
}
5253
}
5354

55+
//Converts a JSON string to a Map<String, String>
56+
static Map<String, String> convertJsonToMap(String jsonString) {
57+
try (JsonReader reader = JsonProviders.createReader(jsonString)) {
58+
reader.nextToken();
59+
return reader.readMap(JsonReader::getString);
60+
} catch (IOException e) {
61+
throw new MsalClientException("Could not parse JSON from HttpResponse body: " + e.getMessage(), AuthenticationErrorCode.INVALID_JSON);
62+
}
63+
}
64+
5465
/**
5566
* Throws exception if given String does not follow JSON syntax
5667
*/

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

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import com.nimbusds.oauth2.sdk.ParseException;
77
import com.nimbusds.oauth2.sdk.SerializeException;
88
import com.nimbusds.oauth2.sdk.util.URLUtils;
9-
import com.nimbusds.openid.connect.sdk.token.OIDCTokens;
109
import org.slf4j.Logger;
1110
import org.slf4j.LoggerFactory;
1211

@@ -121,17 +120,11 @@ private AuthenticationResult createAuthenticationResultFromOauthHttpResponse(
121120
if (oauthHttpResponse.statusCode() == HttpHelper.HTTP_STATUS_200) {
122121
final TokenResponse response = TokenResponse.parseHttpResponse(oauthHttpResponse);
123122

124-
OIDCTokens tokens = response.getOIDCTokens();
125-
String refreshToken = null;
126-
if (tokens.getRefreshToken() != null) {
127-
refreshToken = tokens.getRefreshToken().getValue();
128-
}
129-
130123
AccountCacheEntity accountCacheEntity = null;
131-
if (!StringHelper.isNullOrBlank(tokens.getIDTokenString())) {
124+
if (!StringHelper.isNullOrBlank(response.idToken())) {
132125
String idTokenJson;
133126
try {
134-
idTokenJson = new String(Base64.getDecoder().decode(tokens.getIDTokenString().split("\\.")[1]), StandardCharsets.UTF_8);
127+
idTokenJson = new String(Base64.getDecoder().decode(response.idToken().split("\\.")[1]), StandardCharsets.UTF_8);
135128
} catch (ArrayIndexOutOfBoundsException e) {
136129
throw new MsalServiceException("Error parsing ID token, missing payload section. Ensure that the ID token is following the JWT format.",
137130
AuthenticationErrorCode.INVALID_JWT);
@@ -161,10 +154,10 @@ private AuthenticationResult createAuthenticationResultFromOauthHttpResponse(
161154
long currTimestampSec = new Date().getTime() / 1000;
162155

163156
result = AuthenticationResult.builder().
164-
accessToken(tokens.getAccessToken().getValue()).
165-
refreshToken(refreshToken).
157+
accessToken(response.accessToken()).
158+
refreshToken(response.refreshToken()).
166159
familyId(response.getFoci()).
167-
idToken(tokens.getIDTokenString()).
160+
idToken(response.idToken()).
168161
environment(requestAuthority.host()).
169162
expiresOn(currTimestampSec + response.getExpiresIn()).
170163
extExpiresOn(response.getExtExpiresIn() > 0 ? currTimestampSec + response.getExtExpiresIn() : 0).

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

Lines changed: 30 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -3,108 +3,39 @@
33

44
package com.microsoft.aad.msal4j;
55

6-
import com.nimbusds.oauth2.sdk.ParseException;
7-
import com.nimbusds.oauth2.sdk.token.AccessToken;
8-
import com.nimbusds.oauth2.sdk.token.RefreshToken;
9-
import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
10-
import com.nimbusds.openid.connect.sdk.OIDCTokenResponse;
11-
import com.nimbusds.openid.connect.sdk.token.OIDCTokens;
12-
import net.minidev.json.JSONObject;
6+
import java.util.Map;
137

14-
class TokenResponse extends OIDCTokenResponse {
8+
class TokenResponse {
159

1610
private String scope;
1711
private String clientInfo;
1812
private long expiresIn;
1913
private long extExpiresIn;
2014
private String foci;
2115
private long refreshIn;
22-
23-
TokenResponse(final AccessToken accessToken, final RefreshToken refreshToken, final String idToken,
24-
final String scope, String clientInfo, long expiresIn, long extExpiresIn, String foci,
25-
long refreshIn) {
26-
super(new OIDCTokens(idToken, accessToken, refreshToken));
27-
this.scope = scope;
28-
this.clientInfo = clientInfo;
29-
this.expiresIn = expiresIn;
30-
this.extExpiresIn = extExpiresIn;
31-
this.refreshIn = refreshIn;
32-
this.foci = foci;
16+
private String accessToken;
17+
private String idToken;
18+
private String refreshToken;
19+
20+
TokenResponse(Map<String, String> jsonMap) {
21+
this.accessToken = jsonMap.get("access_token");
22+
this.idToken = jsonMap.get("id_token");
23+
this.refreshToken = jsonMap.get("refresh_token");
24+
this.scope = jsonMap.get("scope");
25+
this.clientInfo = jsonMap.get("client_info");
26+
this.expiresIn = StringHelper.isNullOrBlank(jsonMap.get("expires_in")) ? 0 : Long.parseLong(jsonMap.get("expires_in"));
27+
this.extExpiresIn = StringHelper.isNullOrBlank(jsonMap.get("ext_expires_in")) ? 0 : Long.parseLong(jsonMap.get("ext_expires_in"));
28+
this.refreshIn = StringHelper.isNullOrBlank(jsonMap.get("refresh_in")) ? 0: Long.parseLong(jsonMap.get("refresh_in"));
29+
this.foci = jsonMap.get("foci");
3330
}
3431

35-
static TokenResponse parseHttpResponse(final HttpResponse httpResponse) throws ParseException {
32+
static TokenResponse parseHttpResponse(final HttpResponse httpResponse) {
3633

3734
if (httpResponse.statusCode() != HttpHelper.HTTP_STATUS_200) {
3835
throw MsalServiceExceptionFactory.fromHttpResponse(httpResponse);
3936
}
4037

41-
final JSONObject jsonObject = httpResponse.getBodyAsJson();
42-
43-
return parseJsonObject(jsonObject);
44-
}
45-
46-
static Long getLongValue(JSONObject jsonObject, String key) throws ParseException {
47-
Object value = jsonObject.get(key);
48-
49-
if (value instanceof Long) {
50-
return JSONObjectUtils.getLong(jsonObject, key);
51-
} else {
52-
return Long.parseLong(JSONObjectUtils.getString(jsonObject, key));
53-
}
54-
}
55-
56-
static TokenResponse parseJsonObject(final JSONObject jsonObject)
57-
throws ParseException {
58-
59-
// In same cases such as client credentials there isn't an id token. Instead of a null value
60-
// use an empty string in order to avoid an IllegalArgumentException from OIDCTokens.
61-
String idTokenValue = "";
62-
if (jsonObject.containsKey("id_token")) {
63-
idTokenValue = JSONObjectUtils.getString(jsonObject, "id_token");
64-
}
65-
66-
// Parse value
67-
String scopeValue = null;
68-
if (jsonObject.containsKey("scope")) {
69-
scopeValue = JSONObjectUtils.getString(jsonObject, "scope");
70-
}
71-
72-
String clientInfo = null;
73-
if (jsonObject.containsKey("client_info")) {
74-
clientInfo = JSONObjectUtils.getString(jsonObject, "client_info");
75-
}
76-
77-
long expiresIn = 0;
78-
if (jsonObject.containsKey("expires_in")) {
79-
expiresIn = getLongValue(jsonObject, "expires_in");
80-
}
81-
82-
long ext_expires_in = 0;
83-
if (jsonObject.containsKey("ext_expires_in")) {
84-
ext_expires_in = getLongValue(jsonObject, "ext_expires_in");
85-
}
86-
87-
String foci = null;
88-
if (jsonObject.containsKey("foci")) {
89-
foci = JSONObjectUtils.getString(jsonObject, "foci");
90-
}
91-
92-
long refreshIn = 0;
93-
if (jsonObject.containsKey("refresh_in")) {
94-
refreshIn = getLongValue(jsonObject, "refresh_in");
95-
}
96-
97-
try {
98-
final AccessToken accessToken = AccessToken.parse(jsonObject);
99-
final RefreshToken refreshToken = RefreshToken.parse(jsonObject);
100-
return new TokenResponse(accessToken, refreshToken, idTokenValue, scopeValue, clientInfo,
101-
expiresIn, ext_expires_in, foci, refreshIn);
102-
} catch (ParseException e) {
103-
throw new MsalClientException("Invalid or missing token, could not parse. If using B2C, information on a potential B2C issue and workaround can be found here: https://aka.ms/msal4j-b2c-known-issues",
104-
AuthenticationErrorCode.INVALID_JSON);
105-
} catch (Exception e) {
106-
throw new MsalClientException(e);
107-
}
38+
return new TokenResponse(httpResponse.getBodyAsMap());
10839
}
10940

11041
String getScope() {
@@ -130,4 +61,16 @@ String getFoci() {
13061
long getRefreshIn() {
13162
return this.refreshIn;
13263
}
64+
65+
public String accessToken() {
66+
return accessToken;
67+
}
68+
69+
public String idToken() {
70+
return idToken;
71+
}
72+
73+
public String refreshToken() {
74+
return refreshToken;
75+
}
13376
}

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public void tokenCacheEntitiesFormatTest(String folder) throws URISyntaxExceptio
158158
doReturn(msalOAuthHttpRequest).when(request).createOauthHttpRequest();
159159
doReturn(httpResponse).when(msalOAuthHttpRequest).send();
160160
doReturn(200).when(httpResponse).statusCode();
161-
doReturn(JSONObjectUtils.parse(tokenResponse)).when(httpResponse).getBodyAsJson();
161+
doReturn(JsonHelper.convertJsonToMap((tokenResponse))).when(httpResponse).getBodyAsMap();
162162

163163
final AuthenticationResult result = request.executeTokenRequest();
164164

@@ -186,14 +186,24 @@ private void validateAccessTokenCacheEntity(String folder, String tokenResponse,
186186
String valueExpected = readResource(folder + AT_CACHE_ENTITY);
187187

188188
JSONObject tokenResponseJsonObj = JSONObjectUtils.parse(tokenResponse);
189-
long expireIn = TokenResponse.getLongValue(tokenResponseJsonObj, "expires_in");
189+
long expireIn = getLongValue(tokenResponseJsonObj, "expires_in");
190190

191-
long extExpireIn = TokenResponse.getLongValue(tokenResponseJsonObj, "ext_expires_in");
191+
long extExpireIn = getLongValue(tokenResponseJsonObj, "ext_expires_in");
192192

193193
JSONAssert.assertEquals(valueExpected, valueActual,
194194
new DynamicTimestampsComparator(JSONCompareMode.STRICT, expireIn, extExpireIn));
195195
}
196196

197+
static Long getLongValue(JSONObject jsonObject, String key) throws ParseException {
198+
Object value = jsonObject.get(key);
199+
200+
if (value instanceof Long) {
201+
return JSONObjectUtils.getLong(jsonObject, key);
202+
} else {
203+
return Long.parseLong(JSONObjectUtils.getString(jsonObject, key));
204+
}
205+
}
206+
197207
private void validateRefreshTokenCacheEntity(String folder, TokenCache tokenCache)
198208
throws IOException, URISyntaxException, JSONException {
199209

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ private PublicClientApplication getClientApplicationMockedWithOneTokenEndpointRe
9898
switch (responseType) {
9999
case RETRY_AFTER_HEADER:
100100
httpResponse.statusCode(HTTPResponse.SC_OK);
101-
httpResponse.body(TestConfiguration.TOKEN_ENDPOINT_OK_RESPONSE);
101+
httpResponse.body(TestConfiguration.TOKEN_ENDPOINT_OK_RESPONSE_ID_AND_ACCESS);
102102

103103
headers.put("Retry-After", Arrays.asList(THROTTLE_IN_SEC.toString()));
104104
break;

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public final class TestConfiguration {
4343

4444
public final static String AAD_PREFERRED_NETWORK_ENV_ALIAS = "login.microsoftonline.com";
4545

46-
public final static String TOKEN_ENDPOINT_OK_RESPONSE = "{\"access_token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6I"
46+
public final static String TOKEN_ENDPOINT_OK_RESPONSE_ID_AND_ACCESS = "{\"access_token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6I"
4747
+ "k5HVEZ2ZEstZnl0aEV1THdqcHdBSk9NOW4tQSJ9.eyJhdWQiOiJiN2E2NzFkOC1hNDA4LTQyZmYtODZlMC1hYWY0NDdmZDE3YzQiLCJpc3MiOiJod"
4848
+ "HRwczovL3N0cy53aW5kb3dzLm5ldC8zMGJhYTY2Ni04ZGY4LTQ4ZTctOTdlNi03N2NmZDA5OTU5NjMvIiwiaWF0IjoxMzkzODQ0NTA0LCJuYmYiOj"
4949
+ "EzOTM4NDQ1MDQsImV4cCI6MTM5Mzg0ODQwNCwidmVyIjoiMS4wIiwidGlkIjoiMzBiYWE2NjYtOGRmOC00OGU3LTk3ZTYtNzdjZmQwOTk1OTYzIiwi"
@@ -66,6 +66,21 @@ public final class TestConfiguration {
6666
"WQiOiI5ZjQ4ODBkOC04MGJhLTRjNDAtOTdiYy1mN2EyM2M3MDMwODQiLCJ1dGlkIjoiZjY0NWFkOTItZTM4ZC00ZDFhLWI1MTAtZDFiMDlhNzRhOGNhIn0\"" +
6767
"}";
6868

69+
public final static String TOKEN_ENDPOINT_OK_RESPONSE_ACCESS_ONLY = "{\"token_type\":\"Bearer\",\"expires_in\":3600," +
70+
"\"access_token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1THdqcHdBSk9NOW4tQSJ9" +
71+
".eyJhdWQiOiJiN2E2NzFkOC1hNDA4LTQyZmYtODZlMC1hYWY0NDdmZDE3YzQiLCJpc3MiOiJodRwczovL3N0cy53aW5kb3dzLm5ldC8" +
72+
"zMGJhYTY2Ni04ZGY4LTQ4ZTctOTdlNi03N2NmZDA5OTU5NjMvIiwiaWF0IjoxMzkzODQ0NTA0LCJuYmYiOjEzOTM4NDQ1MDQsImV4cC" +
73+
"I6MTM5Mzg0ODQwNCwidmVyIjoiMS4wIiwidGlkIjoiMzBiYWE2NjYtOGRmOC00OGU3LTk3ZTYtNzdjZmQwOTk1OTYzIiwib2lkIjoiN" +
74+
"GY4NTk5ODktYTJmZi00MTFlLTkwNDgtYzMyMjI0N2FjNjJjIiwidXBuIjoiYWRtaW5AYWFsdGVzdHMub25taWNyb3NvZnQuY29tIiwi" +
75+
"dW5pcXVlX25hbWUiOiJhZG1pbkBhYWx0ZXN0cy5vbm1pY3Jvc29mdC5jb20iLCJzdWIiOiJqQ0ttUENWWEFzblN1MVBQUzRWblo4c2V" +
76+
"ONTR3U3F0cW1RkpGbW14SEF3IiwiZmFtaWx5X25hbWUiOiJBZG1pbiIsImdpdmVuX25hbWUiOiJBREFMVGVzdHMiLCJhcHBpZCI6Ijk" +
77+
"wODNjY2I4LThhNDYtNDNlNy04NDM5LTFkNjk2ZGY5ODRhZSIsImFwcGlkYWNyIjoiMSIsInNjcCI6InVzZXJfaW1wZXJzb25hdGlvbi" +
78+
"IsImFjciI6IjEifQ.lUfDlkLdNGuAGUukgTnS_uWeSFXljbhId1l9PDrr7AwOSbOzogLvO14TaU294T6HeOQ8e0dUAvxEAMvsK_800A" +
79+
"-AGNvbHK363xDjgmu464ye3CQvwq73GoHkzuxILCJKo0DUj0_XsCpQ4TdkPepuhzaGc-zYsfMU1POuIOB87pzW7e_VDpCdxcN1fuk-7" +
80+
"CECPQb8nrO0L8Du8y-TZDdTSe-i_A0Alv48Zll-6tDY9cxfAR0UyYKl_Kf45kuHAphCWwPsjUxv4rGHhgXZPRlKFq7bkXP2Es4ixCQz" +
81+
"b3bVLLrtQaZjkQ1yn37ngJro8NR63EbHHjHTA9lRmf8KIQ\"" +
82+
"}";
83+
6984
public final static String HTTP_ERROR_RESPONSE = "{\"error\":\"invalid_request\",\"error_description\":\"AADSTS90011: Request "
7085
+ "is ambiguous, multiple application identifiers found. Application identifiers: 'd09bb6da-4d46-4a16-880c-7885d8291fb9"
7186
+ ", 216ef81d-f3b2-47d4-ad21-a4df49b56dee'.\r\nTrace ID: 428a1f68-767d-4a1c-ae8e-f710eeaf4e9b\r\nCorrelation ID: 1e0955"

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
package com.microsoft.aad.msal4j;
55

6+
import com.azure.json.JsonProviders;
7+
import com.azure.json.JsonReader;
68
import com.nimbusds.jose.*;
79
import com.nimbusds.jose.crypto.RSASSASigner;
810
import com.nimbusds.jose.jwk.RSAKey;

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import com.nimbusds.oauth2.sdk.ParseException;
77
import com.nimbusds.oauth2.sdk.SerializeException;
8-
import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
98
import org.junit.jupiter.api.Test;
109
import org.junit.jupiter.api.TestInstance;
1110
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -235,7 +234,7 @@ void testExecuteOAuth_Success() throws SerializeException, ParseException, MsalE
235234

236235
doReturn(msalOAuthHttpRequest).when(request).createOauthHttpRequest();
237236
doReturn(httpResponse).when(msalOAuthHttpRequest).send();
238-
doReturn(JSONObjectUtils.parse(TestConfiguration.TOKEN_ENDPOINT_OK_RESPONSE)).when(httpResponse).getBodyAsJson();
237+
doReturn(JsonHelper.convertJsonToMap(TestConfiguration.TOKEN_ENDPOINT_OK_RESPONSE_ID_AND_ACCESS)).when(httpResponse).getBodyAsMap();
239238

240239
doReturn(200).when(httpResponse).statusCode();
241240

0 commit comments

Comments
 (0)