[java-idp-plugin-oidc-rp] branch main updated: Add individual requested claims action

Phil Smart philip.smart at jisc.ac.uk
Thu Jul 21 13:11:40 UTC 2022


This is an automated email from the git hooks/post-receive script.

philsmart pushed a commit to branch main
in repository java-idp-plugin-oidc-rp.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-oidc-rp.git;a=commit;h=f5d15a0453165588ed1194a2fa2fcca57e1c38eb

The following commit(s) were added to refs/heads/main by this push:
     new f5d15a0  Add individual requested claims action
f5d15a0 is described below

commit f5d15a0453165588ed1194a2fa2fcca57e1c38eb
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Jul 21 14:11:35 2022 +0100

    Add individual requested claims action
    
    Is just a hook for extension
---
 .../authn/oidc/rp/impl/AddRequestedClaims.java     | 101 ++++++++++++++++
 .../authn/oidc/rp/impl/BuildRequestObject.java     |  24 ++--
 .../idp/plugin/authn/oidc/rp/impl/DecryptJWT.java  |  21 ++++
 .../oidc-relying-party-authn-beans.xml             |  26 ++++-
 .../oidc-relying-party-authn-flow.xml              |   1 +
 .../resources/templates/oidc-request-form-post.vm  |   4 +-
 .../authn/oidc/rp/impl/AbstractOIDCTest.java       |  14 ++-
 .../authn/oidc/rp/impl/AddRequestedClaimsTest.java | 107 +++++++++++++++++
 .../oidc/rp/impl/AuthorizationControllerTest.java  |   2 -
 .../authn/oidc/rp/impl/BuildRequestObjectTest.java | 130 +++++++++++++++++++++
 .../flow/AbstractAuthnXmlFlowExecutionTests.java   |   6 +-
 .../test-provider-requestobject-encrypt.json       |   1 +
 12 files changed, 417 insertions(+), 20 deletions(-)

diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddRequestedClaims.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddRequestedClaims.java
new file mode 100644
index 0000000..855edcd
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddRequestedClaims.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.openid.connect.sdk.OIDCClaimsRequest;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.FunctionSupport;
+
+/** 
+ * An action that adds requested claims to the under constructions authentication request.
+ * 
+ * <p>The claims are added from a customizable strategy/hook. No additional claims are provided by default.</p>
+ */
+public class AddRequestedClaims extends AbstractOIDCAuthenticationRequestAction {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(AddRequestedClaims.class);
+    
+    /** A hook that creates requested claims JSON Object from the profile request object.*/
+    @Nonnull private Function<ProfileRequestContext, OIDCClaimsRequest> requestedClaimsHook;
+    
+    /** Constructor.*/
+    public AddRequestedClaims() {
+        requestedClaimsHook = FunctionSupport.constant(null);
+    }
+    
+    /**
+     * Set the hook that generates a requested claims JSON Object from the given profile request object.
+     * 
+     * @param hook the hook
+     */
+    public void setRequestedClaimsHook(@Nullable final Function<ProfileRequestContext, OIDCClaimsRequest> hook) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+        if (hook != null) {
+            requestedClaimsHook = hook;
+        }
+    }
+    
+    @Override
+    protected boolean doPreExecute(final ProfileRequestContext profileRequestContext,
+            final AuthenticationContext authenticationContext) {
+        
+        if (!super.doPreExecute(profileRequestContext, authenticationContext)) {
+            return false;
+        } 
+        
+        if (!getProviderMetadata().supportsClaimsParam()) {
+            log.trace("{} Downstream OpenID Provider does not support the 'claims' parameter", getLogPrefix());
+            return false;
+        }
+        
+        return true;
+    }
+    
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+        final OIDCClaimsRequest requestedClaims = requestedClaimsHook.apply(profileRequestContext);
+        if (requestedClaims != null) {
+            getAuthenticationRequest().setRequestedClaims(requestedClaims);
+            log.trace("{} Added requested claims '{}' to the authentication request for client '{}'",getLogPrefix(),
+                    requestedClaims, getAuthenticationRequest().getClientID());
+        } else {
+            log.trace("{} No individual 'claims' requested claims", getLogPrefix());
+        }
+        
+        
+    }
+    
+    
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/BuildRequestObject.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/BuildRequestObject.java
index 1e3f666..bb77907 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/BuildRequestObject.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/BuildRequestObject.java
@@ -109,6 +109,8 @@ public class BuildRequestObject extends AbstractOIDCAuthenticationRequestAction
             @Nonnull final AuthenticationContext authenticationContext) {
         
         log.debug("{} Building a plain RequestObject JWT", getLogPrefix());
+        
+        final OIDCAuthenticationRequest authnRequest = getAuthenticationRequest();        
         final ClaimsSet requestObjectClaims = new ClaimsSet();     
         
         if (requestObjectToBeSignedPredicate.test(profileRequestContext)) {
@@ -122,19 +124,23 @@ public class BuildRequestObject extends AbstractOIDCAuthenticationRequestAction
                 ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_AUTHN_CTX);
                 return;
             }
-            requestObjectClaims.setIssuer(new Issuer(getAuthenticationRequest().getClientID().getValue())); 
+            requestObjectClaims.setIssuer(new Issuer(authnRequest.getClientID().getValue())); 
         }
         
-        
+        //TODO lots of possible NPEs here?
         requestObjectClaims.setClaim("client_id", getAuthenticationRequest().getClientID().toString());    
-        requestObjectClaims.setClaim("nonce", getAuthenticationRequest().getNonce().getValue());        
-        requestObjectClaims.setClaim("response_type", getAuthenticationRequest().getResponseType().toString());
+        requestObjectClaims.setClaim("nonce", authnRequest.getNonce().getValue());        
+        requestObjectClaims.setClaim("response_type", authnRequest.getResponseType().toString());
         // Only set the response_mode if not equal to the default for that response_type
-        if (!getAuthenticationRequest().getDefaultResponseMode().equals(getAuthenticationRequest().getResponseMode())){
-            requestObjectClaims.setClaim("response_mode", getAuthenticationRequest().getResponseMode());
+        if (!authnRequest.getDefaultResponseMode().equals(authnRequest.getResponseMode())){
+            requestObjectClaims.setClaim("response_mode", authnRequest.getResponseMode());
+        }
+        requestObjectClaims.setClaim("redirect_uri", authnRequest.getRedirectURI().toString());
+        requestObjectClaims.setClaim("scope", authnRequest.getScope().toString());
+        
+        if (authnRequest.getRequestedClaims() != null) {
+            requestObjectClaims.setClaim("claims", authnRequest.getRequestedClaims());
         }
-        requestObjectClaims.setClaim("redirect_uri", getAuthenticationRequest().getRedirectURI().toString());
-        requestObjectClaims.setClaim("scope", getAuthenticationRequest().getScope().toString());
         
         // Validate the request object
         if (!validateRequestObject(profileRequestContext, requestObjectClaims)) {
@@ -149,7 +155,7 @@ public class BuildRequestObject extends AbstractOIDCAuthenticationRequestAction
         }
         
         // Create a plain JWT at first, can be signed and encrypted later        
-        getAuthenticationRequest().setRequestObjectClaimsSet(requestObjectClaims);
+        authnRequest.setRequestObjectClaimsSet(requestObjectClaims);
                
     }
     
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DecryptJWT.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DecryptJWT.java
index aae31b5..e4b1319 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DecryptJWT.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DecryptJWT.java
@@ -17,6 +17,7 @@
 
 package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
 
+import java.text.ParseException;
 import java.util.function.BiConsumer;
 import java.util.function.Function;
 
@@ -32,6 +33,8 @@ import org.opensaml.xmlsec.encryption.support.DecryptionException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.nimbusds.jose.JWEObject;
+import com.nimbusds.jose.JWEObject.State;
 import com.nimbusds.jwt.EncryptedJWT;
 import com.nimbusds.jwt.JWT;
 
@@ -41,6 +44,8 @@ import net.shibboleth.oidc.security.JWTDecryptionParameters;
 import net.shibboleth.oidc.security.context.JWTSecurityParametersContext;
 import net.shibboleth.oidc.security.impl.JWTDecrypter;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.codec.Base64Support;
+import net.shibboleth.utilities.java.support.codec.DecodingException;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
@@ -165,6 +170,9 @@ public class DecryptJWT extends AbstractProfileAction {
         try {
             final JWT decryptedJWT = decrypter.decrypt(encryptedJwt);
             log.debug("{} JWT decrypted successfully", getLogPrefix());
+            if (log.isTraceEnabled()) {
+                logJWT(decryptedJWT);
+            }            
             jwtUpdateStrategy.accept(profileRequestContext, decryptedJWT);
         } catch (final DecryptionException e) {
             log.error("{} Unable to decrypt JWT", getLogPrefix(), e);
@@ -172,5 +180,18 @@ public class DecryptJWT extends AbstractProfileAction {
             return;
         }     
     }
+    
+    /**
+     * Log (on trace) the JWT. 
+     * 
+     * @param jwt the JWT to log.
+     */
+    private void logJWT(@Nonnull final JWT jwt) {
+        try {
+            log.trace("{} Decrypted JWT: {}", getLogPrefix(), jwt.getJWTClaimsSet().toString());
+        } catch (final IllegalStateException | ParseException e) {
+            log.trace("{} Unable to print decrypted JWT: {}", getLogPrefix(), e.getMessage());
+        }        
+    }
 
 }
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
index 4fa23fa..e82f0e6 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
@@ -121,8 +121,32 @@
     <bean id="AddEndpointURI" scope="prototype"
         class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AddEndpointURI"
         p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
-        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />        
+        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
         
+    <bean id="AddRequestedClaims" scope="prototype"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AddRequestedClaims"
+        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" 
+        p:requestedClaimsHook="#{getObject('shibboleth.oidc.rp.RequestedClaimsHook')}" />
+   
+   <!-- TEST request claims hook -->
+   <bean id="shibboleth.oidc.rp.RequestedClaimsHook" parent="shibboleth.Functions.Scripted" 
+        factory-method="inlineScript"
+        p:inputType="org.opensaml.profile.context.ProfileRequestContext"
+        p:outputType="com.nimbusds.openid.connect.sdk.OIDCClaimsRequest">
+        <constructor-arg>
+        <value>
+        <![CDATA[
+            var requestedClaims =  new com.nimbusds.openid.connect.sdk.OIDCClaimsRequest()
+                .withIDTokenClaimsRequest(new com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest().add("given_name"))
+                .withUserInfoClaimsRequest(new com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest().add("family_name"))
+            requestedClaims;
+         ]]>
+        </value>
+    </constructor-arg>
+   
+   </bean>
+                 
     <bean id="AddRedirectURI" scope="prototype"
         class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AddRedirectURI"
         p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
index 9354e25..a43ae1d 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
@@ -39,6 +39,7 @@
         <evaluate expression="AddNonce" />
         <evaluate expression="AddForceAuthenticationPrompt" />
         <evaluate expression="AddEndpointURI" />
+        <evaluate expression="AddRequestedClaims" />
         <evaluate expression="AddRedirectURI"/>
         <evaluate expression="AddAuthenticationContextClassReferences" />
         <!-- <evaluate expression="PostRequestPopulateAuditContext" /> <evaluate expression="WriteAuditLog" /> -->
diff --git a/idp-oidc-rp-impl/src/main/resources/templates/oidc-request-form-post.vm b/idp-oidc-rp-impl/src/main/resources/templates/oidc-request-form-post.vm
index e8381a2..8afcbfd 100644
--- a/idp-oidc-rp-impl/src/main/resources/templates/oidc-request-form-post.vm
+++ b/idp-oidc-rp-impl/src/main/resources/templates/oidc-request-form-post.vm
@@ -32,8 +32,10 @@
             <input type="hidden" name="state" value="${state}" />#end #if($prompt)
 
             <input type="hidden" name="prompt" value="${prompt}" />#end #if($request)
+            
+            <input type="hidden" name="request" value="${request}" />#end #if($claims)
 
-            <input type="hidden" name="request" value="${request}" />#end #if($nonce)
+            <input type="hidden" name="claims" value="${claims}" />#end #if($nonce)
             
             <input type="hidden" name="nonce" value="${nonce}" />#end
         </div>
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractOIDCTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractOIDCTest.java
index 69e084d..44c8db9 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractOIDCTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractOIDCTest.java
@@ -140,6 +140,9 @@ public abstract class AbstractOIDCTest {
     /** The request context to use.*/
     protected OIDCPeerEntityContext peerEntityCtx;
     
+    /** The authentication request.*/
+    protected OIDCAuthenticationRequest authnRequest;
+    
     /** 
      * Setup the various contexts.
      * 
@@ -158,10 +161,10 @@ public abstract class AbstractOIDCTest {
         ac.addSubcontext(prc);
         
         final MessageContext outMsgCtx = new MessageContext();
-        final OIDCAuthenticationRequest request = new OIDCAuthenticationRequest(new ClientID("https://rp.example.com"));
-        request.setState(new State("8df98fd63a53fa5b5433d6f8754bca5d.65317332"));
-        request.setRedirectURI(new URI("https://rp.example.com/callback"));
-        outMsgCtx.setMessage(request);
+        authnRequest = new OIDCAuthenticationRequest(new ClientID("https://rp.example.com"));
+        authnRequest.setState(new State("8df98fd63a53fa5b5433d6f8754bca5d.65317332"));
+        authnRequest.setRedirectURI(new URI("https://rp.example.com/callback"));
+        outMsgCtx.setMessage(authnRequest);
         prc.setOutboundMessageContext(outMsgCtx);      
 
         // FIXME we should no longer need this context
@@ -180,8 +183,7 @@ public abstract class AbstractOIDCTest {
         peerEntityCtx.addSubcontext(providerCtx);     
         outMsgCtx.addSubcontext(peerEntityCtx);
         
-        //Set the inbound reponse.
-        
+        //Set the inbound reponse.        
         final MessageContext inMsgCtx = new MessageContext();
         inMsgCtx.setMessage(AuthenticationResponseParser.parse(
                 new URI("/idp/profile/Authn/OIDC/RP/callback"
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddRequestedClaimsTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddRequestedClaimsTest.java
new file mode 100644
index 0000000..b0814c9
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddRequestedClaimsTest.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.context.navigate.ParentContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.webflow.execution.Event;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.openid.connect.sdk.OIDCClaimsRequest;
+import com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+import net.shibboleth.idp.relyingparty.RelyingPartyConfiguration;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+/** Tests for the AddRequestedClaims action.*/
+public class AddRequestedClaimsTest extends AbstractOIDCTest {
+    
+    /** The action to test.*/
+    private AddRequestedClaims action;
+    
+    /** The RPC.*/
+    private RelyingPartyContext rpc;
+    
+    /** The profile config.*/
+    private OIDCAuthorizationConfiguration oidcAuthzConfig;
+    
+    @Override
+    @BeforeMethod
+    public void setup() throws Exception {
+        super.setup();
+        action = new AddRequestedClaims(); 
+        
+        rpc = prc.getSubcontext(RelyingPartyContext.class, true); 
+        oidcAuthzConfig = new OIDCAuthorizationConfiguration();
+        final RelyingPartyConfiguration rpConfig = new RelyingPartyConfiguration();
+        rpc.setProfileConfig(oidcAuthzConfig);
+        rpc.setConfiguration(rpConfig);
+        
+        action.setProfileContextLookupStrategy(new ChildContextLookup<>(ProfileRequestContext.class).compose(
+                new ChildContextLookup<>(AuthenticationContext.class)
+                .compose(new WebflowRequestContextProfileRequestContextLookup())));     
+        
+        action.setAuthenticationContextLookupStrategy(new ParentContextLookup<>(AuthenticationContext.class));
+    }
+    
+    @Test
+    public void testAddRequestedClaims() throws ComponentInitializationException {
+
+        action.setRequestedClaimsHook(prc -> {
+            final OIDCClaimsRequest claims =  new OIDCClaimsRequest()
+                    .withIDTokenClaimsRequest(new VerifiedClaimsSetRequest().add("given_name"))
+                    .withUserInfoClaimsRequest(new VerifiedClaimsSetRequest().add("family_name"));
+            return claims; 
+        });
+        
+        action.initialize();
+        
+        final Event event = action.execute(src);
+        assertNull(event);
+        assertNotNull(prc.getOutboundMessageContext().getMessage());
+        assertNotNull(((OIDCAuthenticationRequest)prc.getOutboundMessageContext()
+                .getMessage()).getRequestedClaims());    
+        assertNotNull(((OIDCAuthenticationRequest)prc.getOutboundMessageContext()
+                .getMessage()).getRequestedClaims().getIDTokenClaimsRequest().get("given_name", null));
+        assertNotNull(((OIDCAuthenticationRequest)prc.getOutboundMessageContext()
+                .getMessage()).getRequestedClaims().getUserInfoClaimsRequest().get("family_name", null));
+    }
+    
+    @Test
+    public void testNoRequestedClaims() throws ComponentInitializationException {
+
+        action.initialize();
+        
+        final Event event = action.execute(src);
+        assertNull(event);
+        assertNotNull(prc.getOutboundMessageContext().getMessage());
+        assertNull(((OIDCAuthenticationRequest)prc.getOutboundMessageContext()
+                .getMessage()).getRequestedClaims());    
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationControllerTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationControllerTest.java
index a52124b..8ec7aeb 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationControllerTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationControllerTest.java
@@ -28,7 +28,6 @@ import static org.testng.Assert.assertTrue;
 import java.io.IOException;
 import java.net.URI;
 import java.net.URLEncoder;
-import java.security.KeyException;
 import java.util.ArrayList;
 
 import javax.annotation.Nonnull;
@@ -70,7 +69,6 @@ import org.springframework.webflow.executor.FlowExecutorImpl;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import com.nimbusds.jose.JOSEException;
 import com.nimbusds.jose.JWEAlgorithm;
 import com.nimbusds.jose.jwk.KeyUse;
 import com.nimbusds.jose.jwk.RSAKey;
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/BuildRequestObjectTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/BuildRequestObjectTest.java
new file mode 100644
index 0000000..958bd7f
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/BuildRequestObjectTest.java
@@ -0,0 +1,130 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.context.navigate.ParentContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.webflow.execution.Event;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.nimbusds.oauth2.sdk.ResponseMode;
+import com.nimbusds.oauth2.sdk.ResponseType;
+import com.nimbusds.openid.connect.sdk.Nonce;
+import com.nimbusds.openid.connect.sdk.OIDCClaimsRequest;
+import com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest;
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+import net.shibboleth.idp.relyingparty.RelyingPartyConfiguration;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+/** Tests for the {@link BuildRequestObject} action.*/
+public class BuildRequestObjectTest extends AbstractOIDCTest {
+    
+    /** The action to test.*/
+    private BuildRequestObject action;
+    
+    /** The RPC.*/
+    private RelyingPartyContext rpc;
+    
+    /** The profile config.*/
+    private OIDCAuthorizationConfiguration oidcAuthzConfig;
+       
+    @Override
+    @BeforeMethod
+    public void setup() throws Exception {
+        super.setup();
+        action = new BuildRequestObject(); 
+        
+        rpc = prc.getSubcontext(RelyingPartyContext.class, true); 
+        oidcAuthzConfig = new OIDCAuthorizationConfiguration();
+        final RelyingPartyConfiguration rpConfig = new RelyingPartyConfiguration();
+        rpc.setProfileConfig(oidcAuthzConfig);
+        rpc.setConfiguration(rpConfig);
+        
+        action.setProfileContextLookupStrategy(new ChildContextLookup<>(ProfileRequestContext.class).compose(
+                new ChildContextLookup<>(AuthenticationContext.class)
+                .compose(new WebflowRequestContextProfileRequestContextLookup())));     
+        
+        action.setAuthenticationContextLookupStrategy(new ParentContextLookup<>(AuthenticationContext.class));
+        
+        // Setup a basic authentication request
+        authnRequest.setNonce(new Nonce());
+        authnRequest.setResponseType(ResponseType.CODE);
+        authnRequest.setResponseMode(ResponseMode.QUERY);
+        authnRequest.setDefaultResponseMode(ResponseMode.QUERY);
+    }
+    
+    @Test
+    public void testBuildRequestObject_Success() throws ComponentInitializationException {
+        
+        action.initialize();        
+        final Event event = action.execute(src);
+        assertNull(event);
+        assertNotNull(prc.getOutboundMessageContext().getMessage());
+        assertNotNull(((OIDCAuthenticationRequest)prc.getOutboundMessageContext()
+                .getMessage()).getRequestObjectClaimsSet());
+        final ClaimsSet claims = (((OIDCAuthenticationRequest)prc.getOutboundMessageContext()
+                .getMessage()).getRequestObjectClaimsSet());
+        assertEquals(claims.getStringClaim("scope"),"openid");
+        assertEquals(claims.getStringClaim("iss"),"https://rp.example.com");
+        assertEquals(claims.getStringClaim("client_id"),"https://rp.example.com");
+        assertEquals(claims.getStringClaim("response_type"),"code");
+        assertEquals(claims.getStringClaim("redirect_uri"),"https://rp.example.com/callback");
+        assertNotNull(claims.getStringClaim("nonce"));
+        assertNotNull(claims.getClaim("aud"));
+        
+        assertTrue(isValidJSON(claims.toJSONString()));
+    }
+    
+    @Test
+    public void testBuildRequestObject_WithRequestedClaims_Success() throws ComponentInitializationException {
+        
+        final OIDCClaimsRequest requestedClaims =  new OIDCClaimsRequest()
+                .withIDTokenClaimsRequest(new VerifiedClaimsSetRequest().add("given_name"))
+                .withUserInfoClaimsRequest(new VerifiedClaimsSetRequest().add("family_name"));
+        authnRequest.setRequestedClaims(requestedClaims);
+        
+        action.initialize();        
+        final Event event = action.execute(src);
+        assertNull(event);
+        assertNotNull(prc.getOutboundMessageContext().getMessage());
+        assertNotNull(((OIDCAuthenticationRequest)prc.getOutboundMessageContext()
+                .getMessage()).getRequestObjectClaimsSet());
+        final ClaimsSet claims = (((OIDCAuthenticationRequest)prc.getOutboundMessageContext()
+                .getMessage()).getRequestObjectClaimsSet());
+        assertEquals(claims.getStringClaim("scope"),"openid");
+        assertEquals(claims.getStringClaim("iss"),"https://rp.example.com");
+        assertEquals(claims.getStringClaim("client_id"),"https://rp.example.com");
+        assertEquals(claims.getStringClaim("response_type"),"code");
+        assertEquals(claims.getStringClaim("redirect_uri"),"https://rp.example.com/callback");
+        assertNotNull(claims.getStringClaim("nonce"));
+        assertNotNull(claims.getClaim("aud"));
+        assertNotNull(claims.getClaim("claims"));
+        assertTrue(isValidJSON(claims.getClaim("claims").toString()));
+        
+        assertTrue(isValidJSON(claims.toJSONString()));
+    }
+    
+    private boolean isValidJSON(final String json)  {
+        try{ 
+            final ObjectMapper objectMapper = new ObjectMapper();
+            objectMapper.readTree(json);
+        } catch(final Exception e){
+            return false;
+        }
+        return true;
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/test/flow/AbstractAuthnXmlFlowExecutionTests.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/test/flow/AbstractAuthnXmlFlowExecutionTests.java
index 7003e42..75cbf1b 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/test/flow/AbstractAuthnXmlFlowExecutionTests.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/test/flow/AbstractAuthnXmlFlowExecutionTests.java
@@ -341,7 +341,11 @@ public abstract class AbstractAuthnXmlFlowExecutionTests extends CustomAbstractX
         
         addBeanDefinition(builderContext, "shibboleth.Functions.Expression",BeanDefinitionBuilder.
                 genericBeanDefinition(net.shibboleth.ext.spring.util.SpringExpressionFunction.class)
-                .setAbstract(true).getBeanDefinition());        
+                .setAbstract(true).getBeanDefinition());
+        
+        addBeanDefinition(builderContext, "shibboleth.Functions.Scripted",BeanDefinitionBuilder.
+                genericBeanDefinition(net.shibboleth.utilities.java.support.logic.ScriptedFunction.class)
+                .setAbstract(true).getBeanDefinition());
         
         addBeanDefinition(builderContext, "shibboleth.BiFunctions.Expression",BeanDefinitionBuilder.
                 genericBeanDefinition(net.shibboleth.ext.spring.util.SpringExpressionBiFunction.class)
diff --git a/idp-oidc-rp-impl/src/test/resources/metadata/test-provider-requestobject-encrypt.json b/idp-oidc-rp-impl/src/test/resources/metadata/test-provider-requestobject-encrypt.json
index 17c2156..02633d9 100644
--- a/idp-oidc-rp-impl/src/test/resources/metadata/test-provider-requestobject-encrypt.json
+++ b/idp-oidc-rp-impl/src/test/resources/metadata/test-provider-requestobject-encrypt.json
@@ -6,6 +6,7 @@
    "userinfo_endpoint":"https://localhost:9921/v1/userinfo",
    "revocation_endpoint":"https://localhost:9921/revoke",
    "jwks_uri":"https://localhost:9921/oauth2/v3/certs",
+   "claims_parameter_supported":true,
    "request_parameter_supported":true,
    "response_types_supported":[
       "code",

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list