Skip to content

Support for claims request parameter #315

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Dec 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,10 @@ abstract class AbstractMsalAuthorizationGrant {
String getScopes() {
return scopes;
}

ClaimsRequest claims;

ClaimsRequest getClaims() {
return claims;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ AuthenticationResult execute() throws Exception {
(IntegratedWindowsAuthorizationGrant) authGrant;
msalRequest.msalAuthorizationGrant =
new OAuthAuthorizationGrant(getAuthorizationGrantIntegrated(
integratedAuthGrant.getUserName()), integratedAuthGrant.getScopes());
integratedAuthGrant.getUserName()), integratedAuthGrant.getScopes(), integratedAuthGrant.getClaims());
}

if (requestAuthority == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ private AuthenticationResult acquireTokenWithAuthorizationCode(AuthorizationResu
.builder(authorizationResult.code(), interactiveRequest.interactiveRequestParameters().redirectUri())
.scopes(interactiveRequest.interactiveRequestParameters().scopes())
.codeVerifier(interactiveRequest.verifier())
.claims(interactiveRequest.interactiveRequestParameters().claims())
.build();

AuthorizationCodeRequest authCodeRequest = new AuthorizationCodeRequest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public class AuthorizationCodeParameters implements IApiParameters {
*/
private Set<String> scopes;

/**
* Claims to be requested through the OIDC claims request parameter, allowing requests for standard and custom claims
*/
private ClaimsRequest claims;

/**
* Code verifier used for PKCE. For more details, see https://tools.ietf.org/html/rfc7636
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ private static AbstractMsalAuthorizationGrant createMsalGrant(AuthorizationCodeP
new AuthorizationCode(parameters.authorizationCode()),parameters.redirectUri());
}

return new OAuthAuthorizationGrant(authorizationGrant, parameters.scopes());
return new OAuthAuthorizationGrant(authorizationGrant, parameters.scopes(), parameters.claims());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ private AuthorizationRequestUrlParameters(Builder builder){
requestParameters.put("claims", Collections.singletonList(builder.claimsChallenge));
}

if(builder.claimsRequest != null){
String claimsRequest = builder.claimsRequest.formatAsJSONString();
//If there are other claims (such as part of a claims challenge), merge them with this claims request.
if (requestParameters.get("claims") != null) {
claimsRequest = JsonHelper.mergeJSONString(claimsRequest, requestParameters.get("claims").get(0));
}
requestParameters.put("claims", Collections.singletonList(claimsRequest));
}

if(builder.codeChallenge != null){
this.codeChallenge = builder.codeChallenge;
requestParameters.put("code_challenge", Collections.singletonList(builder.codeChallenge));
Expand Down Expand Up @@ -154,6 +163,7 @@ public static class Builder {
private Set<String> extraScopesToConsent;
private Set<String> claims;
private String claimsChallenge;
private ClaimsRequest claimsRequest;
private String codeChallenge;
private String codeChallengeMethod;
private String state;
Expand Down Expand Up @@ -202,22 +212,17 @@ public Builder extraScopesToConsent(Set<String> val){
* In cases where Azure AD tenant admin has enabled conditional access policies, and the
* policy has not been met,{@link MsalServiceException} will contain claims that need be
* consented to.
*
* Deprecated in favor of {@link #claimsChallenge(String)}
*/
@Deprecated
public Builder claims(Set<String> val){
this.claims = val;
public Builder claimsChallenge(String val){
this.claimsChallenge = val;
return self();
}

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

Expand Down
78 changes: 78 additions & 0 deletions src/main/java/com/microsoft/aad/msal4j/ClaimsRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.microsoft.aad.msal4j;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;

/**
* Represents the claims request parameter as an object
*
* @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>
*/
public class ClaimsRequest {

@Getter
@Setter
List<RequestedClaim> idTokenRequestedClaims = new ArrayList<>();

List<RequestedClaim> userInfoRequestedClaims = new ArrayList<>();
List<RequestedClaim> accessTokenRequestedClaims = new ArrayList<>();

/**
* Inserts a claim into the list of claims to be added to the "id_token" section of an OIDC claims request
*
* @param claim the name of the claim to be requested
* @param requestedClaimAdditionalInfo additional information about the claim being requested
*/
public void requestClaimInIdToken(String claim, RequestedClaimAdditionalInfo requestedClaimAdditionalInfo) {
idTokenRequestedClaims.add(new RequestedClaim(claim, requestedClaimAdditionalInfo));
}

/**
* Inserts a claim into the list of claims to be added to the "access_token" section of an OIDC claims request
*
* @param claim the name of the claim to be requested
* @param requestedClaimAdditionalInfo additional information about the claim being requested
*/
protected void requestClaimInAccessToken(String claim, RequestedClaimAdditionalInfo requestedClaimAdditionalInfo) {
accessTokenRequestedClaims.add(new RequestedClaim(claim, requestedClaimAdditionalInfo));
}

/**
* Converts the ClaimsRequest object to a JSON-formatted String which follows the specification for the OIDC claims request parameter
*
* @return a String following JSON formatting
*/
public String formatAsJSONString() {
ObjectMapper mapper = new ObjectMapper();
ObjectNode rootNode = mapper.createObjectNode();

if (!idTokenRequestedClaims.isEmpty()) {
rootNode.set("id_token", convertClaimsToObjectNode(idTokenRequestedClaims));
}
if (!userInfoRequestedClaims.isEmpty()) {
rootNode.set("userinfo", convertClaimsToObjectNode(userInfoRequestedClaims));
}
if (!accessTokenRequestedClaims.isEmpty()) {
rootNode.set("access_token", convertClaimsToObjectNode(accessTokenRequestedClaims));
}

return mapper.valueToTree(rootNode).toString();
}

private ObjectNode convertClaimsToObjectNode(List<RequestedClaim> claims) {
ObjectMapper mapper = new ObjectMapper();
ObjectNode claimsNode = mapper.createObjectNode();

for (RequestedClaim claim: claims) {
claimsNode.setAll((ObjectNode) mapper.valueToTree(claim));
}
return claimsNode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public class ClientCredentialParameters implements IApiParameters {
@NonNull
private Set<String> scopes;

/**
* Claims to be requested through the OIDC claims request parameter, allowing requests for standard and custom claims
*/
private ClaimsRequest claims;

private static ClientCredentialParametersBuilder builder() {

return new ClientCredentialParametersBuilder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@

import com.nimbusds.oauth2.sdk.ClientCredentialsGrant;

import java.util.Set;

class ClientCredentialRequest extends MsalRequest{

ClientCredentialRequest(ClientCredentialParameters parameters,
Expand All @@ -17,6 +15,6 @@ class ClientCredentialRequest extends MsalRequest{

private static OAuthAuthorizationGrant createMsalGrant(ClientCredentialParameters parameters){

return new OAuthAuthorizationGrant(new ClientCredentialsGrant(), parameters.scopes());
return new OAuthAuthorizationGrant(new ClientCredentialsGrant(), parameters.scopes(), parameters.claims());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ class DeviceCodeAuthorizationGrant extends AbstractMsalAuthorizationGrant {
*
* @param scopes The resource for which the device code was acquired.
*/
DeviceCodeAuthorizationGrant(DeviceCode deviceCode, final String scopes) {
DeviceCodeAuthorizationGrant(DeviceCode deviceCode, final String scopes, ClaimsRequest claims) {
this.deviceCode = deviceCode;
this.correlationId = deviceCode.correlationId();
this.scopes = scopes;
this.claims = claims;
}

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

return outParams;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public class DeviceCodeFlowParameters implements IApiParameters {
@NonNull
private Set<String> scopes;

/**
* Claims to be requested through the OIDC claims request parameter, allowing requests for standard and custom claims
*/
private ClaimsRequest claims;

/**
* Receives the device code returned from the first step of Oauth2.0 device code flow. The
* {@link DeviceCode#verificationUri} and the {@link DeviceCode#userCode} should be shown
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ DeviceCode acquireDeviceCode(String url,
}

void createAuthenticationGrant(DeviceCode deviceCode) {
msalAuthorizationGrant = new DeviceCodeAuthorizationGrant(deviceCode, deviceCode.scopes());
msalAuthorizationGrant = new DeviceCodeAuthorizationGrant(deviceCode, deviceCode.scopes(), parameters.claims());
}

private String createQueryParams(String clientId) {
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/microsoft/aad/msal4j/IApiParameters.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@

interface IApiParameters {
Set<String> scopes();

ClaimsRequest claims();
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public class IntegratedWindowsAuthenticationParameters implements IApiParameters
@NonNull
private Set<String> scopes;

/**
* Claims to be requested through the OIDC claims request parameter, allowing requests for standard and custom claims
*/
private ClaimsRequest claims;

/**
* Identifier of user account for which to acquire tokens for
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ class IntegratedWindowsAuthenticationRequest extends MsalRequest{
private static AbstractMsalAuthorizationGrant createAuthenticationGrant
(IntegratedWindowsAuthenticationParameters parameters){

return new IntegratedWindowsAuthorizationGrant(parameters.scopes(), parameters.username());
return new IntegratedWindowsAuthorizationGrant(parameters.scopes(), parameters.username(), parameters.claims());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ class IntegratedWindowsAuthorizationGrant extends AbstractMsalAuthorizationGrant

private final String userName;

IntegratedWindowsAuthorizationGrant(Set<String> scopes, String userName) {
IntegratedWindowsAuthorizationGrant(Set<String> scopes, String userName, ClaimsRequest claims) {
this.userName = userName;
this.scopes = String.join(" ", scopes);
this.claims = claims;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ public class InteractiveRequestParameters implements IApiParameters {
@NonNull
private URI redirectUri;

/**
* Claims to be requested through the OIDC claims request parameter, allowing requests for standard and custom claims
*/
private ClaimsRequest claims;

/**
* Scopes that the application is requesting access to and the user will consent to.
*/
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/com/microsoft/aad/msal4j/JsonHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.fasterxml.jackson.databind.JsonNode;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;

Expand Down Expand Up @@ -55,7 +56,11 @@ static void validateJsonFormat(String jsonString) {
*/
public static String formCapabilitiesJson(Set<String> clientCapabilities) {
if (clientCapabilities != null && !clientCapabilities.isEmpty()) {
return "{\"access_token\":{\"xms_cc\":{\"values\":[\"" + String.join("\",\"", clientCapabilities) + "\"]}}}";
ClaimsRequest cr = new ClaimsRequest();
RequestedClaimAdditionalInfo capabilitiesValues = new RequestedClaimAdditionalInfo(false, null, new ArrayList<>(clientCapabilities));
cr.requestClaimInAccessToken("xms_cc", capabilitiesValues);

return cr.formatAsJSONString();
}
else {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ private OAuthAuthorizationGrant() {
params.put(SCOPE_PARAM_NAME, Collections.singletonList(COMMON_SCOPES_PARAM));
}

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

OAuthAuthorizationGrant(final AuthorizationGrant grant, String scopes) {
OAuthAuthorizationGrant(final AuthorizationGrant grant, String scopes, ClaimsRequest claims) {
this();
this.grant = grant;

Expand All @@ -39,6 +39,11 @@ private OAuthAuthorizationGrant() {
params.put(SCOPE_PARAM_NAME,
Collections.singletonList(String.join(" ",params.get(SCOPE_PARAM_NAME)) + SCOPES_DELIMITER + scopes));
}

if (claims != null) {
this.claims = claims;
params.put("claims", Collections.singletonList(claims.formatAsJSONString()));
}
}

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

return Collections.unmodifiableMap(outParams);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ public class OnBehalfOfParameters implements IApiParameters {
@NonNull
private Set<String> scopes;

/**
* Claims to be requested through the OIDC claims request parameter, allowing requests for standard and custom claims
*/
private ClaimsRequest claims;

@NonNull
private IUserAssertion userAssertion;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ private static OAuthAuthorizationGrant createAuthenticationGrant(OnBehalfOfParam
Map<String, List<String>> params = new HashMap<>();
params.put("scope", Collections.singletonList(String.join(" ", parameters.scopes())));
params.put("requested_token_use", Collections.singletonList("on_behalf_of"));
if (parameters.claims() != null) {
params.put("claims", Collections.singletonList(parameters.claims().formatAsJSONString()));
}

return new OAuthAuthorizationGrant(jWTBearerGrant, params);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ public class RefreshTokenParameters implements IApiParameters {
@NonNull
private Set<String> scopes;

/**
* Claims to be requested through the OIDC claims request parameter, allowing requests for standard and custom claims
*/
private ClaimsRequest claims;

/**
* Refresh token received from the STS
*/
Expand Down
Loading