Skip to content

Commit 383c0fc

Browse files
authored
Support for claims request parameter (#315)
* ClaimsRequest classes * Support for claims request parameter * Tests for claims request * Use Jackson library for JSON processing * Change access level of userinfo and access_token claims * Better merge tests * Remove ability to set claims in userinfo field * Refactor claims field naming
1 parent 2e2d1f2 commit 383c0fc

31 files changed

+334
-25
lines changed

src/main/java/com/microsoft/aad/msal4j/AbstractMsalAuthorizationGrant.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,10 @@ abstract class AbstractMsalAuthorizationGrant {
3434
String getScopes() {
3535
return scopes;
3636
}
37+
38+
ClaimsRequest claims;
39+
40+
ClaimsRequest getClaims() {
41+
return claims;
42+
}
3743
}

src/main/java/com/microsoft/aad/msal4j/AcquireTokenByAuthorizationGrantSupplier.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ AuthenticationResult execute() throws Exception {
4848
(IntegratedWindowsAuthorizationGrant) authGrant;
4949
msalRequest.msalAuthorizationGrant =
5050
new OAuthAuthorizationGrant(getAuthorizationGrantIntegrated(
51-
integratedAuthGrant.getUserName()), integratedAuthGrant.getScopes());
51+
integratedAuthGrant.getUserName()), integratedAuthGrant.getScopes(), integratedAuthGrant.getClaims());
5252
}
5353

5454
if (requestAuthority == null) {

src/main/java/com/microsoft/aad/msal4j/AcquireTokenByInteractiveFlowSupplier.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ private AuthenticationResult acquireTokenWithAuthorizationCode(AuthorizationResu
149149
.builder(authorizationResult.code(), interactiveRequest.interactiveRequestParameters().redirectUri())
150150
.scopes(interactiveRequest.interactiveRequestParameters().scopes())
151151
.codeVerifier(interactiveRequest.verifier())
152+
.claims(interactiveRequest.interactiveRequestParameters().claims())
152153
.build();
153154

154155
AuthorizationCodeRequest authCodeRequest = new AuthorizationCodeRequest(

src/main/java/com/microsoft/aad/msal4j/AuthorizationCodeParameters.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ public class AuthorizationCodeParameters implements IApiParameters {
4141
*/
4242
private Set<String> scopes;
4343

44+
/**
45+
* Claims to be requested through the OIDC claims request parameter, allowing requests for standard and custom claims
46+
*/
47+
private ClaimsRequest claims;
48+
4449
/**
4550
* Code verifier used for PKCE. For more details, see https://tools.ietf.org/html/rfc7636
4651
*/

src/main/java/com/microsoft/aad/msal4j/AuthorizationCodeRequest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ private static AbstractMsalAuthorizationGrant createMsalGrant(AuthorizationCodeP
3030
new AuthorizationCode(parameters.authorizationCode()),parameters.redirectUri());
3131
}
3232

33-
return new OAuthAuthorizationGrant(authorizationGrant, parameters.scopes());
33+
return new OAuthAuthorizationGrant(authorizationGrant, parameters.scopes(), parameters.claims());
3434
}
3535
}

src/main/java/com/microsoft/aad/msal4j/AuthorizationRequestUrlParameters.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,15 @@ private AuthorizationRequestUrlParameters(Builder builder){
8181
requestParameters.put("claims", Collections.singletonList(builder.claimsChallenge));
8282
}
8383

84+
if(builder.claimsRequest != null){
85+
String claimsRequest = builder.claimsRequest.formatAsJSONString();
86+
//If there are other claims (such as part of a claims challenge), merge them with this claims request.
87+
if (requestParameters.get("claims") != null) {
88+
claimsRequest = JsonHelper.mergeJSONString(claimsRequest, requestParameters.get("claims").get(0));
89+
}
90+
requestParameters.put("claims", Collections.singletonList(claimsRequest));
91+
}
92+
8493
if(builder.codeChallenge != null){
8594
this.codeChallenge = builder.codeChallenge;
8695
requestParameters.put("code_challenge", Collections.singletonList(builder.codeChallenge));
@@ -154,6 +163,7 @@ public static class Builder {
154163
private Set<String> extraScopesToConsent;
155164
private Set<String> claims;
156165
private String claimsChallenge;
166+
private ClaimsRequest claimsRequest;
157167
private String codeChallenge;
158168
private String codeChallengeMethod;
159169
private String state;
@@ -202,22 +212,17 @@ public Builder extraScopesToConsent(Set<String> val){
202212
* In cases where Azure AD tenant admin has enabled conditional access policies, and the
203213
* policy has not been met,{@link MsalServiceException} will contain claims that need be
204214
* consented to.
205-
*
206-
* Deprecated in favor of {@link #claimsChallenge(String)}
207215
*/
208-
@Deprecated
209-
public Builder claims(Set<String> val){
210-
this.claims = val;
216+
public Builder claimsChallenge(String val){
217+
this.claimsChallenge = val;
211218
return self();
212219
}
213220

214221
/**
215-
* In cases where Azure AD tenant admin has enabled conditional access policies, and the
216-
* policy has not been met,{@link MsalServiceException} will contain claims that need be
217-
* consented to.
222+
* Claims to be requested through the OIDC claims request parameter, allowing requests for standard and custom claims
218223
*/
219-
public Builder claimsChallenge(String val){
220-
this.claimsChallenge = val;
224+
public Builder claims(ClaimsRequest val){
225+
this.claimsRequest = val;
221226
return self();
222227
}
223228

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.aad.msal4j;
5+
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.fasterxml.jackson.databind.node.ObjectNode;
8+
import lombok.Getter;
9+
import lombok.Setter;
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
13+
/**
14+
* Represents the claims request parameter as an object
15+
*
16+
* @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>
17+
*/
18+
public class ClaimsRequest {
19+
20+
@Getter
21+
@Setter
22+
List<RequestedClaim> idTokenRequestedClaims = new ArrayList<>();
23+
24+
List<RequestedClaim> userInfoRequestedClaims = new ArrayList<>();
25+
List<RequestedClaim> accessTokenRequestedClaims = new ArrayList<>();
26+
27+
/**
28+
* Inserts a claim into the list of claims to be added to the "id_token" section of an OIDC claims request
29+
*
30+
* @param claim the name of the claim to be requested
31+
* @param requestedClaimAdditionalInfo additional information about the claim being requested
32+
*/
33+
public void requestClaimInIdToken(String claim, RequestedClaimAdditionalInfo requestedClaimAdditionalInfo) {
34+
idTokenRequestedClaims.add(new RequestedClaim(claim, requestedClaimAdditionalInfo));
35+
}
36+
37+
/**
38+
* Inserts a claim into the list of claims to be added to the "access_token" section of an OIDC claims request
39+
*
40+
* @param claim the name of the claim to be requested
41+
* @param requestedClaimAdditionalInfo additional information about the claim being requested
42+
*/
43+
protected void requestClaimInAccessToken(String claim, RequestedClaimAdditionalInfo requestedClaimAdditionalInfo) {
44+
accessTokenRequestedClaims.add(new RequestedClaim(claim, requestedClaimAdditionalInfo));
45+
}
46+
47+
/**
48+
* Converts the ClaimsRequest object to a JSON-formatted String which follows the specification for the OIDC claims request parameter
49+
*
50+
* @return a String following JSON formatting
51+
*/
52+
public String formatAsJSONString() {
53+
ObjectMapper mapper = new ObjectMapper();
54+
ObjectNode rootNode = mapper.createObjectNode();
55+
56+
if (!idTokenRequestedClaims.isEmpty()) {
57+
rootNode.set("id_token", convertClaimsToObjectNode(idTokenRequestedClaims));
58+
}
59+
if (!userInfoRequestedClaims.isEmpty()) {
60+
rootNode.set("userinfo", convertClaimsToObjectNode(userInfoRequestedClaims));
61+
}
62+
if (!accessTokenRequestedClaims.isEmpty()) {
63+
rootNode.set("access_token", convertClaimsToObjectNode(accessTokenRequestedClaims));
64+
}
65+
66+
return mapper.valueToTree(rootNode).toString();
67+
}
68+
69+
private ObjectNode convertClaimsToObjectNode(List<RequestedClaim> claims) {
70+
ObjectMapper mapper = new ObjectMapper();
71+
ObjectNode claimsNode = mapper.createObjectNode();
72+
73+
for (RequestedClaim claim: claims) {
74+
claimsNode.setAll((ObjectNode) mapper.valueToTree(claim));
75+
}
76+
return claimsNode;
77+
}
78+
}

src/main/java/com/microsoft/aad/msal4j/ClientCredentialParameters.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ public class ClientCredentialParameters implements IApiParameters {
2626
@NonNull
2727
private Set<String> scopes;
2828

29+
/**
30+
* Claims to be requested through the OIDC claims request parameter, allowing requests for standard and custom claims
31+
*/
32+
private ClaimsRequest claims;
33+
2934
private static ClientCredentialParametersBuilder builder() {
3035

3136
return new ClientCredentialParametersBuilder();

src/main/java/com/microsoft/aad/msal4j/ClientCredentialRequest.java

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

66
import com.nimbusds.oauth2.sdk.ClientCredentialsGrant;
77

8-
import java.util.Set;
9-
108
class ClientCredentialRequest extends MsalRequest{
119

1210
ClientCredentialRequest(ClientCredentialParameters parameters,
@@ -17,6 +15,6 @@ class ClientCredentialRequest extends MsalRequest{
1715

1816
private static OAuthAuthorizationGrant createMsalGrant(ClientCredentialParameters parameters){
1917

20-
return new OAuthAuthorizationGrant(new ClientCredentialsGrant(), parameters.scopes());
18+
return new OAuthAuthorizationGrant(new ClientCredentialsGrant(), parameters.scopes(), parameters.claims());
2119
}
2220
}

src/main/java/com/microsoft/aad/msal4j/DeviceCodeAuthorizationGrant.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ class DeviceCodeAuthorizationGrant extends AbstractMsalAuthorizationGrant {
2323
*
2424
* @param scopes The resource for which the device code was acquired.
2525
*/
26-
DeviceCodeAuthorizationGrant(DeviceCode deviceCode, final String scopes) {
26+
DeviceCodeAuthorizationGrant(DeviceCode deviceCode, final String scopes, ClaimsRequest claims) {
2727
this.deviceCode = deviceCode;
2828
this.correlationId = deviceCode.correlationId();
2929
this.scopes = scopes;
30+
this.claims = claims;
3031
}
3132

3233
/**
@@ -41,6 +42,9 @@ public Map<String, List<String>> toParameters() {
4142
outParams.put("grant_type", Collections.singletonList(GRANT_TYPE));
4243
outParams.put("device_code", Collections.singletonList(deviceCode.deviceCode()));
4344
outParams.put("client_info", Collections.singletonList("1"));
45+
if (claims != null) {
46+
outParams.put("claims", Collections.singletonList(claims.formatAsJSONString()));
47+
}
4448

4549
return outParams;
4650
}

src/main/java/com/microsoft/aad/msal4j/DeviceCodeFlowParameters.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ public class DeviceCodeFlowParameters implements IApiParameters {
2828
@NonNull
2929
private Set<String> scopes;
3030

31+
/**
32+
* Claims to be requested through the OIDC claims request parameter, allowing requests for standard and custom claims
33+
*/
34+
private ClaimsRequest claims;
35+
3136
/**
3237
* Receives the device code returned from the first step of Oauth2.0 device code flow. The
3338
* {@link DeviceCode#verificationUri} and the {@link DeviceCode#userCode} should be shown

src/main/java/com/microsoft/aad/msal4j/DeviceCodeFlowRequest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ DeviceCode acquireDeviceCode(String url,
5555
}
5656

5757
void createAuthenticationGrant(DeviceCode deviceCode) {
58-
msalAuthorizationGrant = new DeviceCodeAuthorizationGrant(deviceCode, deviceCode.scopes());
58+
msalAuthorizationGrant = new DeviceCodeAuthorizationGrant(deviceCode, deviceCode.scopes(), parameters.claims());
5959
}
6060

6161
private String createQueryParams(String clientId) {

src/main/java/com/microsoft/aad/msal4j/IApiParameters.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77

88
interface IApiParameters {
99
Set<String> scopes();
10+
11+
ClaimsRequest claims();
1012
}

src/main/java/com/microsoft/aad/msal4j/IntegratedWindowsAuthenticationParameters.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ public class IntegratedWindowsAuthenticationParameters implements IApiParameters
2929
@NonNull
3030
private Set<String> scopes;
3131

32+
/**
33+
* Claims to be requested through the OIDC claims request parameter, allowing requests for standard and custom claims
34+
*/
35+
private ClaimsRequest claims;
36+
3237
/**
3338
* Identifier of user account for which to acquire tokens for
3439
*/

src/main/java/com/microsoft/aad/msal4j/IntegratedWindowsAuthenticationRequest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ class IntegratedWindowsAuthenticationRequest extends MsalRequest{
1414
private static AbstractMsalAuthorizationGrant createAuthenticationGrant
1515
(IntegratedWindowsAuthenticationParameters parameters){
1616

17-
return new IntegratedWindowsAuthorizationGrant(parameters.scopes(), parameters.username());
17+
return new IntegratedWindowsAuthorizationGrant(parameters.scopes(), parameters.username(), parameters.claims());
1818
}
1919
}

src/main/java/com/microsoft/aad/msal4j/IntegratedWindowsAuthorizationGrant.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ class IntegratedWindowsAuthorizationGrant extends AbstractMsalAuthorizationGrant
1111

1212
private final String userName;
1313

14-
IntegratedWindowsAuthorizationGrant(Set<String> scopes, String userName) {
14+
IntegratedWindowsAuthorizationGrant(Set<String> scopes, String userName, ClaimsRequest claims) {
1515
this.userName = userName;
1616
this.scopes = String.join(" ", scopes);
17+
this.claims = claims;
1718
}
1819

1920
@Override

src/main/java/com/microsoft/aad/msal4j/InteractiveRequestParameters.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ public class InteractiveRequestParameters implements IApiParameters {
3838
@NonNull
3939
private URI redirectUri;
4040

41+
/**
42+
* Claims to be requested through the OIDC claims request parameter, allowing requests for standard and custom claims
43+
*/
44+
private ClaimsRequest claims;
45+
4146
/**
4247
* Scopes that the application is requesting access to and the user will consent to.
4348
*/

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.fasterxml.jackson.databind.JsonNode;
1212

1313
import java.io.IOException;
14+
import java.util.ArrayList;
1415
import java.util.Iterator;
1516
import java.util.Set;
1617

@@ -55,7 +56,11 @@ static void validateJsonFormat(String jsonString) {
5556
*/
5657
public static String formCapabilitiesJson(Set<String> clientCapabilities) {
5758
if (clientCapabilities != null && !clientCapabilities.isEmpty()) {
58-
return "{\"access_token\":{\"xms_cc\":{\"values\":[\"" + String.join("\",\"", clientCapabilities) + "\"]}}}";
59+
ClaimsRequest cr = new ClaimsRequest();
60+
RequestedClaimAdditionalInfo capabilitiesValues = new RequestedClaimAdditionalInfo(false, null, new ArrayList<>(clientCapabilities));
61+
cr.requestClaimInAccessToken("xms_cc", capabilitiesValues);
62+
63+
return cr.formatAsJSONString();
5964
}
6065
else {
6166
return null;

src/main/java/com/microsoft/aad/msal4j/OAuthAuthorizationGrant.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ private OAuthAuthorizationGrant() {
2525
params.put(SCOPE_PARAM_NAME, Collections.singletonList(COMMON_SCOPES_PARAM));
2626
}
2727

28-
OAuthAuthorizationGrant(final AuthorizationGrant grant, Set<String> scopesSet) {
29-
this(grant, scopesSet != null ? String.join(" ", scopesSet) : null);
28+
OAuthAuthorizationGrant(final AuthorizationGrant grant, Set<String> scopesSet, ClaimsRequest claims) {
29+
this(grant, scopesSet != null ? String.join(" ", scopesSet) : null, claims);
3030
}
3131

32-
OAuthAuthorizationGrant(final AuthorizationGrant grant, String scopes) {
32+
OAuthAuthorizationGrant(final AuthorizationGrant grant, String scopes, ClaimsRequest claims) {
3333
this();
3434
this.grant = grant;
3535

@@ -39,6 +39,11 @@ private OAuthAuthorizationGrant() {
3939
params.put(SCOPE_PARAM_NAME,
4040
Collections.singletonList(String.join(" ",params.get(SCOPE_PARAM_NAME)) + SCOPES_DELIMITER + scopes));
4141
}
42+
43+
if (claims != null) {
44+
this.claims = claims;
45+
params.put("claims", Collections.singletonList(claims.formatAsJSONString()));
46+
}
4247
}
4348

4449
OAuthAuthorizationGrant(final AuthorizationGrant grant,
@@ -56,6 +61,9 @@ public Map<String, List<String>> toParameters() {
5661
outParams.putAll(params);
5762
outParams.put("client_info", Collections.singletonList("1"));
5863
outParams.putAll(grant.toParameters());
64+
if (claims != null) {
65+
outParams.put("claims", Collections.singletonList(claims.formatAsJSONString()));
66+
}
5967

6068
return Collections.unmodifiableMap(outParams);
6169
}

src/main/java/com/microsoft/aad/msal4j/OnBehalfOfParameters.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ public class OnBehalfOfParameters implements IApiParameters {
2525
@NonNull
2626
private Set<String> scopes;
2727

28+
/**
29+
* Claims to be requested through the OIDC claims request parameter, allowing requests for standard and custom claims
30+
*/
31+
private ClaimsRequest claims;
32+
2833
@NonNull
2934
private IUserAssertion userAssertion;
3035

src/main/java/com/microsoft/aad/msal4j/OnBehalfOfRequest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ private static OAuthAuthorizationGrant createAuthenticationGrant(OnBehalfOfParam
3636
Map<String, List<String>> params = new HashMap<>();
3737
params.put("scope", Collections.singletonList(String.join(" ", parameters.scopes())));
3838
params.put("requested_token_use", Collections.singletonList("on_behalf_of"));
39+
if (parameters.claims() != null) {
40+
params.put("claims", Collections.singletonList(parameters.claims().formatAsJSONString()));
41+
}
3942

4043
return new OAuthAuthorizationGrant(jWTBearerGrant, params);
4144
}

src/main/java/com/microsoft/aad/msal4j/RefreshTokenParameters.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ public class RefreshTokenParameters implements IApiParameters {
3131
@NonNull
3232
private Set<String> scopes;
3333

34+
/**
35+
* Claims to be requested through the OIDC claims request parameter, allowing requests for standard and custom claims
36+
*/
37+
private ClaimsRequest claims;
38+
3439
/**
3540
* Refresh token received from the STS
3641
*/

0 commit comments

Comments
 (0)