Skip to content

Commit 0ea4362

Browse files
committed
Use azure-json in claims classes
1 parent 19aaaf1 commit 0ea4362

File tree

4 files changed

+311
-83
lines changed

4 files changed

+311
-83
lines changed

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

Lines changed: 95 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,24 @@
33

44
package com.microsoft.aad.msal4j;
55

6+
import com.azure.json.JsonProviders;
7+
import com.azure.json.JsonReader;
8+
import com.azure.json.JsonSerializable;
9+
import com.azure.json.JsonToken;
10+
import com.azure.json.JsonWriter;
611

7-
import com.fasterxml.jackson.core.type.TypeReference;
8-
import com.fasterxml.jackson.databind.JsonNode;
9-
import com.fasterxml.jackson.databind.ObjectMapper;
10-
import com.fasterxml.jackson.databind.ObjectReader;
11-
import com.fasterxml.jackson.databind.node.ObjectNode;
12-
12+
import java.io.ByteArrayOutputStream;
1313
import java.io.IOException;
14+
import java.nio.charset.StandardCharsets;
1415
import java.util.ArrayList;
15-
import java.util.Iterator;
1616
import java.util.List;
1717

1818
/**
1919
* Represents the claims request parameter as an object
2020
*
2121
* @see <a href="https://openid.net/specs/openid-connect-core-1_0-final.html#ClaimsParameter">https://openid.net/specs/openid-connect-core-1_0-final.html#ClaimsParameter</a>
2222
*/
23-
public class ClaimsRequest {
23+
public class ClaimsRequest implements JsonSerializable<ClaimsRequest> {
2424

2525
List<RequestedClaim> idTokenRequestedClaims = new ArrayList<>();
2626
List<RequestedClaim> userInfoRequestedClaims = new ArrayList<>();
@@ -62,31 +62,47 @@ protected void requestClaimInAccessToken(String claim, RequestedClaimAdditionalI
6262
* @return a String following JSON formatting
6363
*/
6464
public String formatAsJSONString() {
65-
ObjectMapper mapper = new ObjectMapper();
66-
ObjectNode rootNode = mapper.createObjectNode();
65+
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
66+
JsonWriter jsonWriter = JsonProviders.createWriter(outputStream)) {
67+
toJson(jsonWriter);
6768

68-
if (!idTokenRequestedClaims.isEmpty()) {
69-
rootNode.set("id_token", convertClaimsToObjectNode(idTokenRequestedClaims));
70-
}
71-
if (!userInfoRequestedClaims.isEmpty()) {
72-
rootNode.set("userinfo", convertClaimsToObjectNode(userInfoRequestedClaims));
73-
}
74-
if (!accessTokenRequestedClaims.isEmpty()) {
75-
rootNode.set("access_token", convertClaimsToObjectNode(accessTokenRequestedClaims));
69+
jsonWriter.flush();
70+
return outputStream.toString(StandardCharsets.UTF_8.name());
71+
} catch (IOException e) {
72+
throw new MsalClientException("Could not convert ClaimsRequest to string: " + e.getMessage(), AuthenticationErrorCode.INVALID_JSON);
7673
}
74+
}
75+
76+
@Override
77+
public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
78+
jsonWriter.writeStartObject();
7779

78-
return mapper.valueToTree(rootNode).toString();
80+
writeClaimsToJsonWriter(jsonWriter, "id_token", idTokenRequestedClaims);
81+
writeClaimsToJsonWriter(jsonWriter, "userinfo", userInfoRequestedClaims);
82+
writeClaimsToJsonWriter(jsonWriter, "access_token", accessTokenRequestedClaims);
83+
84+
jsonWriter.writeEndObject();
85+
return jsonWriter;
7986
}
8087

81-
private ObjectNode convertClaimsToObjectNode(List<RequestedClaim> claims) {
82-
ObjectMapper mapper = new ObjectMapper();
83-
ObjectNode claimsNode = mapper.createObjectNode();
88+
private void writeClaimsToJsonWriter(JsonWriter jsonWriter, String sectionName, List<RequestedClaim> claims) throws IOException {
89+
if (claims.isEmpty()) {
90+
return;
91+
}
8492

93+
jsonWriter.writeStartObject(sectionName);
8594

8695
for (RequestedClaim claim : claims) {
87-
claimsNode.setAll((ObjectNode) mapper.valueToTree(claim));
96+
if (claim.name != null) {
97+
if (claim.getRequestedClaimAdditionalInfo() != null) {
98+
jsonWriter.writeJsonField(claim.name, claim.getRequestedClaimAdditionalInfo());
99+
} else {
100+
jsonWriter.writeNullField(claim.name);
101+
}
102+
}
88103
}
89-
return claimsNode;
104+
105+
jsonWriter.writeEndObject();
90106
}
91107

92108
/**
@@ -96,49 +112,74 @@ private ObjectNode convertClaimsToObjectNode(List<RequestedClaim> claims) {
96112
* @return a ClaimsRequest instance
97113
*/
98114
public static ClaimsRequest formatAsClaimsRequest(String claims) {
99-
try {
100-
ClaimsRequest cr = new ClaimsRequest();
115+
try (JsonReader jsonReader = JsonProviders.createReader(claims)) {
116+
ClaimsRequest claimsRequest = new ClaimsRequest();
101117

102-
ObjectMapper mapper = new ObjectMapper();
103-
ObjectReader reader = mapper.readerFor(new TypeReference<List<String>>() {
104-
});
105-
106-
JsonNode jsonClaims = mapper.readTree(claims);
118+
return jsonReader.readObject(reader -> {
119+
if (reader.currentToken() != JsonToken.START_OBJECT) {
120+
throw new IllegalStateException("Expected start of object but was " + reader.currentToken());
121+
}
107122

108-
addClaimsFromJsonNode(jsonClaims.get("id_token"), "id_token", cr, reader);
109-
addClaimsFromJsonNode(jsonClaims.get("userinfo"), "userinfo", cr, reader);
110-
addClaimsFromJsonNode(jsonClaims.get("access_token"), "access_token", cr, reader);
123+
while (reader.nextToken() != JsonToken.END_OBJECT) {
124+
parseClaims(reader, claimsRequest, reader.getFieldName());
125+
}
111126

112-
return cr;
127+
return claimsRequest;
128+
});
113129
} catch (IOException e) {
114-
throw new MsalClientException("Could not convert string to ClaimsRequest: " + e.getMessage(), AuthenticationErrorCode.INVALID_JSON);
130+
throw new MsalClientException("Could not convert string to ClaimsRequest: " + e.getMessage(),
131+
AuthenticationErrorCode.INVALID_JSON);
115132
}
116133
}
117134

118-
private static void addClaimsFromJsonNode(JsonNode claims, String group, ClaimsRequest cr, ObjectReader reader) throws IOException {
119-
Iterator<String> claimsIterator;
135+
private static void parseClaims(JsonReader jsonReader, ClaimsRequest claimsRequest, String section) throws IOException {
136+
if (jsonReader.currentToken() != JsonToken.FIELD_NAME) {
137+
jsonReader.nextToken();
138+
}
120139

121-
if (claims != null) {
122-
claimsIterator = claims.fieldNames();
123-
while (claimsIterator.hasNext()) {
124-
String claim = claimsIterator.next();
125-
Boolean essential = null;
140+
jsonReader.nextToken();
141+
if (jsonReader.currentToken() != JsonToken.START_OBJECT) {
142+
throw new IllegalStateException("Expected start of object but was " + jsonReader.currentToken());
143+
}
144+
145+
while (jsonReader.nextToken() != JsonToken.END_OBJECT) {
146+
String claimName = jsonReader.getFieldName();
147+
jsonReader.nextToken();
148+
149+
RequestedClaimAdditionalInfo claimInfo = null;
150+
if (jsonReader.currentToken() == JsonToken.START_OBJECT) {
151+
boolean essential = false;
126152
String value = null;
127153
List<String> values = null;
128-
RequestedClaimAdditionalInfo claimInfo = null;
129154

130-
if (claims.get(claim).has("essential")) essential = claims.get(claim).get("essential").asBoolean();
131-
if (claims.get(claim).has("value")) value = claims.get(claim).get("value").textValue();
132-
if (claims.get(claim).has("values")) values = reader.readValue(claims.get(claim).get("values"));
155+
while (jsonReader.nextToken() != JsonToken.END_OBJECT) {
156+
String fieldName = jsonReader.getFieldName();
157+
jsonReader.nextToken();
158+
159+
switch (fieldName) {
160+
case "essential": essential = jsonReader.getBoolean(); break;
161+
case "value": value = jsonReader.getString(); break;
162+
case "values":
163+
values = new ArrayList<>();
164+
if (jsonReader.currentToken() == JsonToken.START_ARRAY) {
165+
while (jsonReader.nextToken() != JsonToken.END_ARRAY) {
166+
values.add(jsonReader.getString());
167+
}
168+
}
169+
break;
170+
default: jsonReader.skipChildren(); break;
171+
}
172+
}
133173

134-
//'null' is a valid value for RequestedClaimAdditionalInfo, so only initialize it if one of the parameters is not null
135-
if (essential != null || value != null || values != null) {
136-
claimInfo = new RequestedClaimAdditionalInfo(essential == null ? false : essential, value, values);
174+
if (essential || value != null || values != null) {
175+
claimInfo = new RequestedClaimAdditionalInfo(essential, value, values);
137176
}
177+
}
138178

139-
if (group.equals("id_token")) cr.requestClaimInIdToken(claim, claimInfo);
140-
if (group.equals("userinfo")) cr.requestClaimInUserInfo(claim, claimInfo);
141-
if (group.equals("access_token")) cr.requestClaimInAccessToken(claim, claimInfo);
179+
switch (section) {
180+
case "access_token": claimsRequest.requestClaimInAccessToken(claimName, claimInfo); break;
181+
case "id_token": claimsRequest.requestClaimInIdToken(claimName, claimInfo); break;
182+
case "userinfo": claimsRequest.requestClaimInUserInfo(claimName, claimInfo); break;
142183
}
143184
}
144185
}
@@ -150,4 +191,4 @@ public List<RequestedClaim> getIdTokenRequestedClaims() {
150191
public void setIdTokenRequestedClaims(List<RequestedClaim> idTokenRequestedClaims) {
151192
this.idTokenRequestedClaims = idTokenRequestedClaims;
152193
}
153-
}
194+
}

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

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

44
package com.microsoft.aad.msal4j;
55

6-
import com.fasterxml.jackson.annotation.JsonAnyGetter;
7-
import com.fasterxml.jackson.annotation.JsonIgnore;
8-
import com.fasterxml.jackson.annotation.JsonInclude;
6+
import com.azure.json.JsonReader;
7+
import com.azure.json.JsonSerializable;
8+
import com.azure.json.JsonToken;
9+
import com.azure.json.JsonWriter;
910

11+
import java.io.IOException;
1012
import java.util.Collections;
1113
import java.util.Map;
1214

@@ -15,21 +17,56 @@
1517
*
1618
* @see <a href="https://openid.net/specs/openid-connect-core-1_0-final.html#ClaimsParameter">https://openid.net/specs/openid-connect-core-1_0-final.html#ClaimsParameter</a>
1719
*/
18-
@JsonInclude(JsonInclude.Include.NON_NULL)
19-
public class RequestedClaim {
20+
public class RequestedClaim implements JsonSerializable<RequestedClaim> {
2021

21-
@JsonIgnore
2222
public String name;
23+
private RequestedClaimAdditionalInfo requestedClaimAdditionalInfo;
2324

24-
RequestedClaimAdditionalInfo requestedClaimAdditionalInfo;
25+
RequestedClaim() {}
2526

2627
public RequestedClaim(String name, RequestedClaimAdditionalInfo requestedClaimAdditionalInfo) {
2728
this.name = name;
2829
this.requestedClaimAdditionalInfo = requestedClaimAdditionalInfo;
2930
}
3031

31-
@JsonAnyGetter
32+
static RequestedClaim fromJson(JsonReader jsonReader) throws IOException {
33+
RequestedClaim claim = new RequestedClaim();
34+
return jsonReader.readObject(reader -> {
35+
if (reader.currentToken() != JsonToken.START_OBJECT) {
36+
throw new IllegalStateException("Expected start of object but was " + reader.currentToken());
37+
}
38+
39+
claim.name = reader.getFieldName();
40+
41+
RequestedClaimAdditionalInfo info = new RequestedClaimAdditionalInfo(false, null, null);
42+
claim.requestedClaimAdditionalInfo = info.fromJson(reader);
43+
44+
return claim;
45+
});
46+
}
47+
48+
@Override
49+
public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
50+
jsonWriter.writeStartObject();
51+
52+
if (name != null && requestedClaimAdditionalInfo != null) {
53+
jsonWriter.writeString(name);
54+
requestedClaimAdditionalInfo.toJson(jsonWriter);
55+
}
56+
57+
jsonWriter.writeEndObject();
58+
return jsonWriter;
59+
}
60+
61+
RequestedClaimAdditionalInfo getRequestedClaimAdditionalInfo() {
62+
return requestedClaimAdditionalInfo;
63+
}
64+
65+
void setRequestedClaimAdditionalInfo(RequestedClaimAdditionalInfo requestedClaimAdditionalInfo) {
66+
this.requestedClaimAdditionalInfo = requestedClaimAdditionalInfo;
67+
}
68+
3269
protected Map<String, Object> any() {
3370
return Collections.singletonMap(name, requestedClaimAdditionalInfo);
3471
}
35-
}
72+
}

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

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,90 @@
33

44
package com.microsoft.aad.msal4j;
55

6-
import com.fasterxml.jackson.annotation.JsonInclude;
7-
import com.fasterxml.jackson.annotation.JsonProperty;
6+
import com.azure.json.JsonReader;
7+
import com.azure.json.JsonSerializable;
8+
import com.azure.json.JsonToken;
9+
import com.azure.json.JsonWriter;
810

11+
import java.io.IOException;
12+
import java.util.ArrayList;
913
import java.util.List;
1014

1115
/**
1216
* Represents the additional information that can be sent to an authorization server for a request claim in the claim request parameter
1317
*
1418
* @see <a href="https://openid.net/specs/openid-connect-core-1_0-final.html#ClaimsParameter">https://openid.net/specs/openid-connect-core-1_0-final.html#ClaimsParameter</a>
1519
*/
16-
@JsonInclude(JsonInclude.Include.NON_NULL)
17-
public class RequestedClaimAdditionalInfo {
20+
public class RequestedClaimAdditionalInfo implements JsonSerializable<RequestedClaimAdditionalInfo> {
1821

19-
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
20-
@JsonProperty("essential")
21-
boolean essential;
22-
23-
@JsonProperty("value")
24-
String value;
25-
26-
@JsonProperty("values")
27-
List<String> values;
22+
private boolean essential;
23+
private String value;
24+
private List<String> values;
2825

2926
public RequestedClaimAdditionalInfo(boolean essential, String value, List<String> values) {
3027
this.essential = essential;
3128
this.value = value;
3229
this.values = values;
3330
}
3431

32+
public RequestedClaimAdditionalInfo fromJson(JsonReader jsonReader) throws IOException {
33+
if (jsonReader.currentToken() != JsonToken.START_OBJECT) {
34+
jsonReader.nextToken();
35+
if (jsonReader.currentToken() != JsonToken.START_OBJECT) {
36+
throw new IllegalStateException("Expected start of object but was " + jsonReader.currentToken());
37+
}
38+
}
39+
40+
while (jsonReader.nextToken() != JsonToken.END_OBJECT) {
41+
String fieldName = jsonReader.getFieldName();
42+
jsonReader.nextToken();
43+
44+
switch (fieldName) {
45+
case "essential":
46+
essential = jsonReader.getBoolean();
47+
break;
48+
case "value":
49+
value = jsonReader.getString();
50+
break;
51+
case "values":
52+
values = new ArrayList<>();
53+
while (jsonReader.nextToken() != JsonToken.END_ARRAY) {
54+
values.add(jsonReader.getString());
55+
}
56+
break;
57+
default:
58+
jsonReader.skipChildren();
59+
break;
60+
}
61+
}
62+
63+
return this;
64+
}
65+
66+
@Override
67+
public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
68+
jsonWriter.writeStartObject();
69+
70+
if (essential) {
71+
jsonWriter.writeBooleanField("essential", essential);
72+
}
73+
74+
if (value != null) {
75+
jsonWriter.writeStringField("value", value);
76+
}
77+
78+
if (values != null && !values.isEmpty()) {
79+
jsonWriter.writeStartArray("values");
80+
for (String val : values) {
81+
jsonWriter.writeString(val);
82+
}
83+
jsonWriter.writeEndArray();
84+
}
85+
86+
jsonWriter.writeEndObject();
87+
return jsonWriter;
88+
}
89+
3590
public boolean isEssential() {
3691
return this.essential;
3792
}
@@ -55,4 +110,4 @@ public void setValue(String value) {
55110
public void setValues(List<String> values) {
56111
this.values = values;
57112
}
58-
}
113+
}

0 commit comments

Comments
 (0)