[java-idp-oidc] 03/03: JOIDC-142 - Improve Request Object handling and configuration

Henri Mikkonen henri.mikkonen at iki.fi
Tue Mar 28 15:07:36 UTC 2023


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

hjmikkon pushed a commit to branch main
in repository java-idp-oidc.

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

commit 710a7f8c5a3d94770642a284092ed081219d009d
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Tue Mar 28 18:07:21 2023 +0300

    JOIDC-142 - Improve Request Object handling and configuration
    
    https://shibboleth.atlassian.net/browse/JOIDC-142
    
    The authorize flow now respected predicates for request object
    signature and encryption..
    
    The authorize flow tests were refactored to exploit the same dynamic
    JWT security tests that are used for testing JWTs issued by OP: they
    cover all the supported JWS and JWE algorithms and encryption methods,
    including all their combinations + exclusions.
---
 .../idp/flows/oidc/authorize/authorize-beans.xml   |  65 +++-
 .../idp/flows/oidc/authorize/authorize-flow.xml    |   1 +
 .../oidc/op/profile/flow/AuthorizeFlowTest.java    | 283 +--------------
 .../oidc/op/profile/flow/RequestObjectJWETest.java | 401 +++++++++++++++++++++
 .../oidc/op/profile/flow/RequestObjectJWSTest.java | 241 +++++++++++++
 .../src/test/resources/conf/relying-party.xml      |  18 +
 6 files changed, 738 insertions(+), 271 deletions(-)

diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
index 549378c6..5ab3c083 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
@@ -112,6 +112,45 @@
         p:httpClient="#{getObject('shibboleth.oidc.NonBrowser.HttpClient') ?: getObject('shibboleth.InternalHttpClient')}"
         p:httpClientSecurityParameters="#{getObject('shibboleth.oidc.NonBrowser.HttpClientSecurityParameters')}" />
 
+    <bean id="RequestObjectEncryptedCondition" parent="shibboleth.Conditions.Expression"
+        c:expression="#input.getOutboundMessageContext().getSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext)).getRequestObject() instanceof T(com.nimbusds.jwt.EncryptedJWT)" />
+
+    <bean id="CheckClientJWTDecryptionConfiguration"
+        class="net.shibboleth.oidc.security.impl.CheckClientJWTDecryptionConfiguration" scope="prototype">
+        <property name="jwtTokenLookupStrategy">
+            <bean
+                class="net.shibboleth.idp.profile.context.navigate.SpringExpressionContextLookupFunction"
+                c:_0="#{ T(org.opensaml.profile.context.ProfileRequestContext) }"
+                c:outputType="#{T(com.nimbusds.jwt.JWT)}"
+                c:expression="#input.getOutboundMessageContext().getSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext)).getRequestObject()" />
+        </property>
+        <property name="clientInformationLookupStrategy">
+            <bean
+                class="net.shibboleth.idp.profile.context.navigate.SpringExpressionContextLookupFunction"
+                c:_0="#{ T(org.opensaml.profile.context.ProfileRequestContext) }"
+                c:expression="#input.getInboundMessageContext().getSubcontext(T(net.shibboleth.oidc.metadata.context.OIDCMetadataContext)).getClientInformation()" />
+        </property>
+        <property name="encryptionOptionalPredicate">
+            <bean parent="shibboleth.Conditions.NOT">
+                <constructor-arg>
+                    <bean class="net.shibboleth.oidc.profile.config.logic.EncryptRequestObjectPredicate"/>
+                </constructor-arg>
+            </bean>
+        </property>
+        <property name="keyTransportEncryptionAlgorithmLookupStrategy">
+            <bean
+                class="net.shibboleth.oidc.profile.config.navigate.ClientInformationStringValueLookupFunction"
+                c:keyName="request_object_encryption_alg"/>
+        </property>
+        <property name="dataEncryptionAlgorithmLookupStrategy">
+            <bean
+                class="net.shibboleth.oidc.profile.config.navigate.ClientInformationStringValueLookupFunction"
+                c:keyName="request_object_encryption_enc"/>
+        </property>
+        <property name="errorEventId"
+            value="#{T(net.shibboleth.oidc.profile.core.OidcEventIds).INVALID_REQUEST_OBJECT}"/>
+    </bean>
+
     <bean id="DecryptRequestObject" class="net.shibboleth.oidc.security.impl.DecryptJWE" scope="prototype">
         <property name="jwtTokenLookupStrategy">
             <bean class="net.shibboleth.idp.profile.context.navigate.SpringExpressionContextLookupFunction"
@@ -123,16 +162,19 @@
             <bean class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.RequestObjectUpdateStrategy" />
         </property>
         <property name="activationCondition">
-            <bean parent="shibboleth.Conditions.Expression"
-                c:_0="#profileContext.getOutboundMessageContext().getSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext)).getRequestObject() instanceof T(com.nimbusds.jwt.EncryptedJWT)" />
+            <ref bean="RequestObjectEncryptedCondition" />
         </property>
     </bean>
 
     <bean id="RequestObjectSignedCondition" parent="shibboleth.Conditions.Expression"
         c:expression="#input.getOutboundMessageContext().getSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext)).getRequestObject() instanceof T(com.nimbusds.jwt.SignedJWT)" />
 
+    <bean id="UseRequestObjectPredicate" class="net.shibboleth.oidc.profile.config.logic.UseRequestObjectPredicate" />
+
+    <bean id="SignRequestObjectPredicate" class="net.shibboleth.oidc.profile.config.logic.SignRequestObjectPredicate" />
+
     <bean id="ValidateRequestObjectSignature" class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor"
-        scope="prototype" c:executionDirection="INBOUND" p:activationCondition-ref="RequestObjectSignedCondition">
+        scope="prototype" c:executionDirection="INBOUND">
         <constructor-arg>
             <bean class="org.opensaml.messaging.handler.impl.BasicMessageHandlerChain">
                 <property name="handlers">
@@ -178,6 +220,23 @@
                 </property>
             </bean>
         </constructor-arg>
+        <property name="activationCondition">
+            <bean parent="shibboleth.Conditions.OR">
+                <constructor-arg>
+                    <ref bean="RequestObjectSignedCondition" />
+                </constructor-arg>
+                <constructor-arg>
+                    <bean parent="shibboleth.Conditions.AND">
+                        <constructor-arg>
+                            <ref bean="UseRequestObjectPredicate" />
+                        </constructor-arg>
+                        <constructor-arg>
+                            <ref bean="SignRequestObjectPredicate" />
+                        </constructor-arg>
+                    </bean>
+                </constructor-arg>
+            </bean>
+        </property>
     </bean>
 
     <bean id="ValidateRequestObject" class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.ValidateRequestObject"
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml
index 5b8b9c9e..229b43d7 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml
@@ -59,6 +59,7 @@
         <evaluate expression="SetRequestObjectToResponseContext" />
         <evaluate expression="PopulateRequestObjectDecryptionParameters" />
         <evaluate expression="PopulateRequestObjectSignatureValidationParameters" />
+        <evaluate expression="CheckClientJWTDecryptionConfiguration" />
         <evaluate expression="DecryptRequestObject" />
         <evaluate expression="ValidateRequestObjectSignature" />
         <evaluate expression="ValidateRequestObject" />
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
index 2734c11d..ae90c90a 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
@@ -46,7 +46,6 @@ import com.nimbusds.jose.JOSEException;
 import com.nimbusds.jose.JWEAlgorithm;
 import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.jose.jwk.Curve;
-import com.nimbusds.jose.jwk.ECKey;
 import com.nimbusds.jwt.JWT;
 import com.nimbusds.jwt.JWTClaimsSet;
 import com.nimbusds.jwt.PlainJWT;
@@ -1152,7 +1151,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithPlainReqObjectClaimsRequest() throws IOException, SessionException,
             DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
+        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload(clientId, redirectUri));
         assertSuccessRequestObjectWithClaimsRequestResponse(new PlainJWT(ro));
     }
 
@@ -1276,271 +1275,6 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
         Assert.assertNull(successResponse.getIssuer());
     }
 
-    @Test
-    public void testWithHS256SignedReqObjectClaimsRequest() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertSuccessRequestObjectWithClaimsRequestResponse(createSecretJWT(ro, clientSecret));
-    }
-
-    @Test
-    public void testWithHS384SignedReqObjectClaimsRequest() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertSuccessRequestObjectWithClaimsRequestResponse(createSecretJWT(ro, clientSecret, JWSAlgorithm.HS384));
-    }
-
-    @Test
-    public void testWithHS512SignedReqObjectClaimsRequest() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertSuccessRequestObjectWithClaimsRequestResponse(createSecretJWT(ro, clientSecret, JWSAlgorithm.HS512));
-    }
-
-    @Test
-    public void testWithHS256SignedReqObjectClaimsRequest_HS256Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertSuccessRequestObjectWithClaimsRequestResponse(createSecretJWT(ro, clientSecret), JWSAlgorithm.HS256);
-    }
-
-    @Test
-    public void testWithHS256SignedReqObjectClaimsRequest_HS384Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertRequestObjectError(createSecretJWT(ro, clientSecret), JWSAlgorithm.HS384);
-    }
-
-    @Test
-    public void testWithHS384SignedReqObjectClaimsRequest_HS384Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertSuccessRequestObjectWithClaimsRequestResponse(createSecretJWT(ro, clientSecret, JWSAlgorithm.HS384),
-                JWSAlgorithm.HS384);
-    }
-
-    @Test
-    public void testWithHS384SignedReqObjectClaimsRequest_HS512Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertRequestObjectError(createSecretJWT(ro, clientSecret, JWSAlgorithm.HS384), JWSAlgorithm.HS512);
-    }
-
-    @Test
-    public void testWithHS512SignedReqObjectClaimsRequest_HS512Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertSuccessRequestObjectWithClaimsRequestResponse(createSecretJWT(ro, clientSecret, JWSAlgorithm.HS512),
-                JWSAlgorithm.HS512);
-    }
-
-    @Test
-    public void testWithHS512SignedReqObjectClaimsRequest_HS384Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertRequestObjectError(createSecretJWT(ro, clientSecret, JWSAlgorithm.HS512), JWSAlgorithm.HS384);
-    }
-
-    @Test
-    public void testWithRS256SignedReqObjectClaimsRequest() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertSuccessRequestObjectWithClaimsRequestResponse(createPrivateKeyJWT(ro, rsaPrivateKey), null, rsaPublicKey);
-    }
-
-    @Test
-    public void testWithRS384SignedReqObjectClaimsRequest() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertSuccessRequestObjectWithClaimsRequestResponse(createPrivateKeyJWT(ro, rsaPrivateKey, JWSAlgorithm.RS384),
-                null, rsaPublicKey);
-    }
-
-    @Test
-    public void testWithRS512SignedReqObjectClaimsRequest() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertSuccessRequestObjectWithClaimsRequestResponse(createPrivateKeyJWT(ro, rsaPrivateKey, JWSAlgorithm.RS512),
-                null, rsaPublicKey);
-    }
-
-    @Test
-    public void testWithRS256SignedReqObjectClaimsRequest_RS256Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertSuccessRequestObjectWithClaimsRequestResponse(createPrivateKeyJWT(ro, rsaPrivateKey), JWSAlgorithm.RS256,
-                rsaPublicKey);
-    }
-
-    @Test
-    public void testWithRS256SignedReqObjectClaimsRequest_RS384Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertRequestObjectError(createPrivateKeyJWT(ro, rsaPrivateKey), JWSAlgorithm.RS384, rsaPublicKey);
-    }
-
-    @Test
-    public void testWithRS384SignedReqObjectClaimsRequest_RS384Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertSuccessRequestObjectWithClaimsRequestResponse(createPrivateKeyJWT(ro, rsaPrivateKey, JWSAlgorithm.RS384),
-                JWSAlgorithm.RS384, rsaPublicKey);
-    }
-
-    @Test
-    public void testWithRS384SignedReqObjectClaimsRequest_RS512Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertRequestObjectError(createPrivateKeyJWT(ro, rsaPrivateKey, JWSAlgorithm.RS384), JWSAlgorithm.RS512,
-                rsaPublicKey);
-    }
-
-    @Test
-    public void testWithRS512SignedReqObjectClaimsRequest_RS512Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertSuccessRequestObjectWithClaimsRequestResponse(createPrivateKeyJWT(ro, rsaPrivateKey, JWSAlgorithm.RS512),
-                JWSAlgorithm.RS512, rsaPublicKey);
-    }
-
-    @Test
-    public void testWithRS512SignedReqObjectClaimsRequest_RS384Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertRequestObjectError(createPrivateKeyJWT(ro, rsaPrivateKey, JWSAlgorithm.RS512), JWSAlgorithm.RS384,
-                rsaPublicKey);
-    }
-
-    @Test
-    public void testWithES256SignedReqObjectClaimsRequest() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertSuccessRequestObjectWithClaimsRequestResponse(createPrivateKeyJWT(ro, ecKey.toECPrivateKey(),
-                JWSAlgorithm.ES256), null, ecKey.toECPublicKey());
-    }
-
-    @Test
-    public void testWithES384SignedReqObjectClaimsRequest() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        final ECKey ecKey = initializeECKey(Curve.P_384, "384");
-        // ES384 is excluded by the test relying-party.xml
-        assertRequestObjectError(createPrivateKeyJWT(ro, ecKey.toECPrivateKey(),
-                JWSAlgorithm.ES384), null, ecKey.toECPublicKey());
-    }
-
-    @Test
-    public void testWithES512SignedReqObjectClaimsRequest() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        final ECKey ecKey = initializeECKey(Curve.P_521, "512");
-        assertSuccessRequestObjectWithClaimsRequestResponse(createPrivateKeyJWT(ro, ecKey.toECPrivateKey(),
-                JWSAlgorithm.ES512), null, ecKey.toECPublicKey());
-    }
-
-    @Test
-    public void testWithES256SignedReqObjectClaimsRequest_ES256Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertSuccessRequestObjectWithClaimsRequestResponse(createPrivateKeyJWT(ro, ecKey.toECPrivateKey(),
-                JWSAlgorithm.ES256), JWSAlgorithm.ES256, ecKey.toECPublicKey());
-    }
-
-    @Test
-    public void testWithES256SignedReqObjectClaimsRequest_ES384Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        assertRequestObjectError(createPrivateKeyJWT(ro, ecKey.toECPrivateKey(), JWSAlgorithm.ES256),
-                JWSAlgorithm.ES384, ecKey.toECPublicKey());
-    }
-
-    @Test
-    public void testWithES384SignedReqObjectClaimsRequest_ES384Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        final ECKey ecKey = initializeECKey(Curve.P_384, "384");
-        // ES384 is excluded by the test relying-party.xml
-        assertRequestObjectError(createPrivateKeyJWT(ro, ecKey.toECPrivateKey(),
-                JWSAlgorithm.ES384), JWSAlgorithm.ES384, ecKey.toECPublicKey());
-    }
-
-    @Test
-    public void testWithES384SignedReqObjectClaimsRequest_ES512Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        final ECKey ecKey = initializeECKey(Curve.P_384, "384");
-        assertRequestObjectError(createPrivateKeyJWT(ro, ecKey.toECPrivateKey(), JWSAlgorithm.ES384),
-                JWSAlgorithm.ES512, ecKey.toECPublicKey());
-    }
-
-    @Test
-    public void testWithES512SignedReqObjectClaimsRequest_ES512Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        final ECKey ecKey = initializeECKey(Curve.P_521, "521");
-        assertSuccessRequestObjectWithClaimsRequestResponse(createPrivateKeyJWT(ro, ecKey.toECPrivateKey(),
-                JWSAlgorithm.ES512), JWSAlgorithm.ES512, ecKey.toECPublicKey());
-    }
-
-    @Test
-    public void testWithES512SignedReqObjectClaimsRequest_ES384Configured() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        final ECKey ecKey = initializeECKey(Curve.P_521, "521");
-        assertRequestObjectError(createPrivateKeyJWT(ro, ecKey.toECPrivateKey(), JWSAlgorithm.ES512),
-                JWSAlgorithm.ES384, ecKey.toECPublicKey());
-    }
-
-    @SuppressWarnings("deprecation") // JWEAlgorithm.RSA_OAEP is deprecated as it's not recommended
-    @Test
-    public void testWithEncryptedPlainRequestObjectWithKid() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        final PlainJWT jwt = new PlainJWT(ro);
-        final String encryptedJwt = createEncryptedJWT(jwt.serialize(), JWEAlgorithm.RSA_OAEP,
-                EncryptionMethod.A128CBC_HS256, loadEncryptionCredential(), false);
-        assertSuccessRequestObjectWithClaimsRequestResponse(encryptedJwt,
-                null, null, JWEAlgorithm.RSA_OAEP, EncryptionMethod.A128CBC_HS256);
-    }
-
-    @SuppressWarnings("deprecation") // JWEAlgorithm.RSA_OAEP is deprecated as it's not recommended
-    @Test
-    public void testWithEncryptedPlainRequestObjectNoKid() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        final PlainJWT jwt = new PlainJWT(ro);
-        final String encryptedJwt = createEncryptedJWT(jwt.serialize(), JWEAlgorithm.RSA_OAEP,
-                EncryptionMethod.A128CBC_HS256, loadEncryptionCredential(), false);
-        assertSuccessRequestObjectWithClaimsRequestResponse(encryptedJwt,
-                null, null, JWEAlgorithm.RSA_OAEP, EncryptionMethod.A128CBC_HS256);
-    }
-
-    @SuppressWarnings("deprecation") // JWEAlgorithm.RSA_OAEP is deprecated as it's not recommended
-    @Test
-    public void testWithEncryptedSignedRequestObjectWithKid() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        final ECKey ecKey = initializeECKey(Curve.P_521, "521");
-        final SignedJWT jwt = createPrivateKeyJWT(ro, ecKey.toECPrivateKey(), JWSAlgorithm.ES512);
-        final String encryptedJwt = createEncryptedJWT(jwt.serialize(), JWEAlgorithm.RSA_OAEP,
-                EncryptionMethod.A128CBC_HS256, loadEncryptionCredential(), true);
-        assertSuccessRequestObjectWithClaimsRequestResponse(encryptedJwt,
-                JWSAlgorithm.ES512, ecKey.toECPublicKey(), JWEAlgorithm.RSA_OAEP, EncryptionMethod.A128CBC_HS256);
-    }
-
-    @SuppressWarnings("deprecation") // JWEAlgorithm.RSA_OAEP is deprecated as it's not recommended
-    @Test
-    public void testWithEncryptedSignedRequestObjectNoKid() throws IOException,
-            SessionException, JOSEException, DataSealerException, ParseException {
-        final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithClaimsRequestPayload());
-        final ECKey ecKey = initializeECKey(Curve.P_521, "521");
-        final SignedJWT jwt = createPrivateKeyJWT(ro, ecKey.toECPrivateKey(), JWSAlgorithm.ES512);
-        final String encryptedJwt = createEncryptedJWT(jwt.serialize(), JWEAlgorithm.RSA_OAEP,
-                EncryptionMethod.A128CBC_HS256, loadEncryptionCredential(), false);
-        assertSuccessRequestObjectWithClaimsRequestResponse(encryptedJwt,
-                JWSAlgorithm.ES512, ecKey.toECPublicKey(), JWEAlgorithm.RSA_OAEP, EncryptionMethod.A128CBC_HS256);
-    }
-
     @Factory
     public Object[] createIdTokenSecurityTests() {
         return new Object[] {
@@ -1564,7 +1298,20 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
                 };
     }
 
-    protected String getRequestObjectWithClaimsRequestPayload() {
+    @Factory
+    public Object[] createRequestObjectSecurityTests() {
+        return new Object[] {
+                new RequestObjectJWSTest(true),
+                new RequestObjectJWSTest(false),
+                new RequestObjectJWETest(false, false),
+                new RequestObjectJWETest(false, true),
+                new RequestObjectJWETest(true, false),
+                new RequestObjectJWETest(true, true)
+                };
+        
+    }
+
+    protected static String getRequestObjectWithClaimsRequestPayload(final String clientId, final String redirectUri) {
         return "{\n"
                 + "  \"iss\": \"" + clientId + "\",\n"
                 + "  \"response_type\": \"code\",\n"
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RequestObjectJWETest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RequestObjectJWETest.java
new file mode 100644
index 00000000..df93c259
--- /dev/null
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RequestObjectJWETest.java
@@ -0,0 +1,401 @@
+/*
+ * 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.oidc.op.profile.flow;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.text.ParseException;
+import java.util.List;
+
+import org.springframework.webflow.executor.FlowExecutionResult;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.nimbusds.jose.EncryptionMethod;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jwt.EncryptedJWT;
+import com.nimbusds.jwt.JWT;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.PlainJWT;
+import com.nimbusds.oauth2.sdk.Scope;
+import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
+import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
+import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement;
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSetRequest;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
+
+import net.shibboleth.idp.plugin.oidc.op.token.support.AuthorizeCodeClaimsSet;
+import net.shibboleth.oidc.security.credential.BasicJWKCredential;
+import net.shibboleth.utilities.java.support.collection.Pair;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.utilities.java.support.security.DataSealerException;
+
+public class RequestObjectJWETest extends IssuedEncryptedJWTTest {
+
+    String defaultClientIdEncryptionEnforced = "mockClientIdRequestObjectEncryptionEnforced";
+
+    public RequestObjectJWETest(final boolean testSigned, final boolean encryptionOptional) {
+        super(JWT_FETCHING_TYPE.REQUEST_OBJECT, AuthorizeFlowTest.FLOW_ID, testSigned, encryptionOptional);
+    }
+
+    @Override @Test
+    public void testJwtEncryption_noSigAlgNorEncSpecified() throws Exception {
+        // use plain request object
+        final JWT jwt = obtainRequestObject(null, null, null, null, null, null);
+        if (encryptionOptional) {
+            assertSuccessRequestObjectResponse(jwt.serialize(), null, null, null, defaultClientSecret64B, null);
+        } else {
+            assertErrorRequestObjectResponse(jwt.serialize(), null, null, null, defaultClientSecret64B, null);
+        }
+    }
+
+    @Test
+    public void testJwtEncryption_noSigAlgNorEncSpecified_noRequestObject() throws Exception {
+        if (encryptionOptional) {
+            assertSuccessRequestObjectResponse("", null, null, null, defaultClientSecret64B, null);
+        } else {
+            assertErrorRequestObjectResponse("", null, null, null, defaultClientSecret64B, null);
+        }
+    }
+
+    @Test
+    public void testJwtEncryption_noSigAlgNorEncSpecified_signedRequestObject() throws Exception {
+        final JWT jwt = obtainRequestObject(defaultClientSecret64B, null, null, JWSAlgorithm.HS256, null, null);
+        if (encryptionOptional) {
+            assertSuccessRequestObjectResponse(jwt.serialize(), null, null, null, defaultClientSecret64B, null);
+        } else {
+            assertErrorRequestObjectResponse(jwt.serialize(), null, null, null, defaultClientSecret64B, null);
+        }
+    }
+
+    @Test
+    public void testJwtEncryption_noSigAlgNorEncSpecified_encryptedRequestObject() throws Exception {
+        if (testSignedJwt) {
+            for (final JWSAlgorithm jwsAlgorithm : JWS_ALGORITHMS) {
+                for (final JWEAlgorithm jwe : JWE_ALGORITHMS) {
+                    for (final EncryptionMethod method : ENCRYPTION_METHODS) {
+                        final JWT jwt = obtainRequestObject(defaultClientSecret64B, getProviderEncryptionKey(jwe),
+                                getSigningKey(jwsAlgorithm), jwsAlgorithm, jwe, method);
+                        assertSuccessRequestObjectResponse(jwt.serialize(), null, null, null, defaultClientSecret64B,
+                                getSignatureVerificationKey(jwsAlgorithm));
+                    }
+                }
+            }
+        } else {
+            for (final JWEAlgorithm jwe : JWE_ALGORITHMS) {
+                for (final EncryptionMethod method : ENCRYPTION_METHODS) {
+                    final JWT jwt = obtainRequestObject(defaultClientSecret64B, getProviderEncryptionKey(jwe), null,
+                            null, jwe, method);
+                    assertSuccessRequestObjectResponse(jwt.serialize(), null, null, null, defaultClientSecret64B, null);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testRequestObjectEncryption_onlySigAlgNoEncSpecified() throws Exception {
+        final JWT jwt = obtainRequestObject(null, null, rsaPrivateKey, JWSAlgorithm.RS256, null, null);
+        if (encryptionOptional) {
+            assertSuccessRequestObjectResponse(jwt.serialize(), JWSAlgorithm.RS256, null, null, null, rsaPublicKey);
+        } else {
+            assertErrorRequestObjectResponse(jwt.serialize(), JWSAlgorithm.RS256, null, null, null, rsaPublicKey);
+        }
+    }
+
+    protected void assertSecretBasedEncryption(final JWEAlgorithm jweAlgorithm, final EncryptionMethod method) {
+        if (testSignedJwt) {
+            for (final JWSAlgorithm jwsAlgorithm : JWS_ALGORITHMS) {
+                final JWT jwt = obtainRequestObject(defaultClientSecret64B, null, getSigningKey(jwsAlgorithm),
+                        jwsAlgorithm, jweAlgorithm, method);
+                assertSuccessRequestObjectResponse(jwt.serialize(), jwsAlgorithm, jweAlgorithm, method,
+                        defaultClientSecret64B, getSignatureVerificationKey(jwsAlgorithm));
+            }
+        } else {
+            final JWT jwt = obtainRequestObject(defaultClientSecret, null, null, null, jweAlgorithm, method);
+            assertSuccessRequestObjectResponse(jwt.serialize(), null, jweAlgorithm, method, defaultClientSecret, null);
+        }
+    }
+    
+    protected PublicKey getProviderEncryptionKeyViaKeyType(final PublicKey publicKey) {
+        if (publicKey instanceof ECPublicKey) {
+            return loadCredential("/credentials/idp-encryption-ec.jwk").getPublicKey();
+        } else {
+            return loadEncryptionCredential().getPublicKey();
+        }
+    }
+
+    protected PublicKey getRandomEncryptionKey(final JWEAlgorithm jweAlgorithm) {
+        if (JWEAlgorithm.Family.ECDH_ES.contains(jweAlgorithm)) {
+            try {
+                return ecKey.toPublicKey();
+            } catch (JOSEException e) {
+                Assert.fail("Could not obtain a public key from the ECKey object", e);
+            }
+        }
+        return rsaPublicKey;
+    }
+
+    protected PublicKey getProviderEncryptionKey(final JWEAlgorithm jweAlgorithm) {
+        if (JWEAlgorithm.Family.ECDH_ES.contains(jweAlgorithm)) {
+            return loadCredential("/credentials/idp-encryption-ec.jwk").getPublicKey();
+        }
+        return loadEncryptionCredential().getPublicKey();
+    }
+
+    protected void assertPublicKeyBasedEncryption(final PublicKey publicKey, final PrivateKey privateKey,
+            final JWEAlgorithm jweAlgorithm, final EncryptionMethod method) {
+        final PublicKey encryptionKey = getProviderEncryptionKeyViaKeyType(publicKey);
+        if (testSignedJwt) {
+            for (final JWSAlgorithm jwsAlgorithm : JWS_ALGORITHMS) {
+                final JWT jwt = obtainRequestObject(defaultClientSecret64B, encryptionKey, getSigningKey(jwsAlgorithm),
+                        jwsAlgorithm, jweAlgorithm, method);
+                assertSuccessRequestObjectResponse(jwt.serialize(), jwsAlgorithm, jweAlgorithm, method,
+                        defaultClientSecret64B, getSignatureVerificationKey(jwsAlgorithm));
+            }
+        } else {
+            final JWT jwt = obtainRequestObject(defaultClientSecret64B, encryptionKey, privateKey, null, jweAlgorithm,
+                    method);
+            assertSuccessRequestObjectResponse(jwt.serialize(), null, jweAlgorithm, method, defaultClientSecret64B,
+                    publicKey);
+        }
+    }
+    
+    @Override
+    protected void assertNoSymmetricKeyResponse(final JWEAlgorithm jweAlgorithm, final EncryptionMethod method)
+            throws Exception {
+        final PublicKey encryptionKey = getProviderEncryptionKey(jweAlgorithm);
+        if (testSignedJwt) {
+            for (final JWSAlgorithm jwsAlgorithm : JWS_ALGORITHMS) {
+                final JWT jwt = obtainRequestObject(defaultClientSecret64B, rsaPublicKey, getSigningKey(jwsAlgorithm),
+                        jwsAlgorithm, jweAlgorithm, method);
+                Assert.assertNotNull(jwt, "The JWT could not be obtained with JWS alg " + jwsAlgorithm);
+                assertErrorRequestObjectResponse(jwt.serialize(), jwsAlgorithm, jweAlgorithm, method, null,
+                        getSignatureVerificationKey(jwsAlgorithm));
+            }
+        } else {
+            final JWT jwt = obtainRequestObject(defaultClientSecret64B, encryptionKey, rsaPrivateKey, null,
+                    jweAlgorithm, method);
+            assertErrorRequestObjectResponse(jwt.serialize(),
+                    null, jweAlgorithm, method, null, encryptionKey);
+        }
+    }
+
+    @Override
+    protected void assertExcludedAlgorithm(final JWEAlgorithm jweAlgorithm, final EncryptionMethod method) {
+        if (testSignedJwt) {
+            for (final JWSAlgorithm jwsAlgorithm : JWS_ALGORITHMS) {
+                final JWT jwt = obtainRequestObject(defaultClientSecret64B, getProviderEncryptionKey(jweAlgorithm),
+                        getSigningKey(jwsAlgorithm), jwsAlgorithm, jweAlgorithm, method);
+                assertErrorRequestObjectResponse(jwt.serialize(), jwsAlgorithm, jweAlgorithm, method,
+                        defaultClientSecret64B, getSignatureVerificationKey(jwsAlgorithm));
+            }
+        } else {
+            final JWT jwt = obtainRequestObject(defaultClientSecret64B, getProviderEncryptionKey(jweAlgorithm), null,
+                    null, jweAlgorithm, method);
+            Assert.assertTrue(jwt instanceof EncryptedJWT, "Was not encrypted " + jweAlgorithm);
+            assertErrorRequestObjectResponse(jwt.serialize(), null, jweAlgorithm, method, defaultClientSecret64B,
+                    rsaPublicKey);
+        }
+    }
+    
+    @Override
+    protected void assertNoPublicKeyResponse(final JWEAlgorithm jweAlgorithm, final EncryptionMethod method)
+ {
+        final PublicKey encryptionKey = getRandomEncryptionKey(jweAlgorithm);
+        if (testSignedJwt) {
+            for (final JWSAlgorithm jwsAlgorithm : JWS_ALGORITHMS) {
+                final JWT jwt = obtainRequestObject(defaultClientSecret64B, encryptionKey, getSigningKey(jwsAlgorithm),
+                        jwsAlgorithm, jweAlgorithm, method);
+                assertErrorRequestObjectResponse(jwt.serialize(),
+                            jwsAlgorithm, jweAlgorithm, method, defaultClientSecret64B, null);
+            }
+        } else {
+            final JWT jwt = obtainRequestObject(defaultClientSecret64B, encryptionKey, rsaPrivateKey, null,
+                    jweAlgorithm, method);
+            assertErrorRequestObjectResponse(jwt.serialize(), null, jweAlgorithm, method, defaultClientSecret64B, null);
+        }
+    }
+
+    protected void assertErrorRequestObjectResponse(final String requestObject,
+            final JWSAlgorithm requestObjectSigAlg, final JWEAlgorithm requestObjectEncAlg,
+            final EncryptionMethod requestObjectEncMethod, final String clientSecret, final PublicKey publicKey) {
+        request.setMethod("GET");
+        final String clientId = encryptionOptional ? defaultClientId : defaultClientIdEncryptionEnforced;
+        final String redirectUri = "https://example.org/cb";
+        AuthorizeFlowTest.setRequestParameters(request, List.of(new Pair<>("client_id", clientId),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("request", requestObject)));
+        initializeThreadLocals();
+
+        final OIDCClientMetadata metadata = buildMetadataSkeleton();
+        metadata.setScope(new Scope("openid"));
+        metadata.setUserInfoJWSAlg(requestObjectSigAlg);
+        metadata.setUserInfoJWEAlg(requestObjectEncAlg);
+        metadata.setUserInfoJWEEnc(requestObjectEncMethod);
+        if (publicKey != null) {
+            metadata.setJWKSet(super.buildJWKSet(publicKey));
+        }
+        try {
+            metadata.setRedirectionURI(new URI("https://example.org/cb"));
+            storeMetadataObject(storageService, clientId, clientSecret, metadata);
+            setBasicAuth("jdoe", "changeit");
+            final FlowExecutionResult result = flowExecutor.launchExecution(flowId, null, externalContext);
+            removeMetadata(storageService, clientId);
+            Assert.assertEquals(result.getOutcome().getId(), "ErrorView");        
+        } catch (final IOException | URISyntaxException e) {
+            Assert.fail();
+        }
+    }
+    
+    protected void assertSuccessRequestObjectResponse(final String requestObject,
+            final JWSAlgorithm requestObjectSigAlg, final JWEAlgorithm requestObjectEncAlg,
+            final EncryptionMethod requestObjectEncMethod, final String clientSecret, final PublicKey publicKey) {
+        request.setMethod("GET");
+        final String clientId = encryptionOptional ? defaultClientId : defaultClientIdEncryptionEnforced;
+        final String redirectUri = "https://example.org/cb";
+        AuthorizeFlowTest.setRequestParameters(request, List.of(new Pair<>("client_id", clientId),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("request", requestObject)));
+        initializeThreadLocals();
+
+        final OIDCClientMetadata metadata = buildMetadataSkeleton();
+        metadata.setScope(new Scope("openid"));
+        metadata.setUserInfoJWSAlg(requestObjectSigAlg);
+        metadata.setUserInfoJWEAlg(requestObjectEncAlg);
+        metadata.setUserInfoJWEEnc(requestObjectEncMethod);
+        if (publicKey != null) {
+            metadata.setJWKSet(super.buildJWKSet(publicKey));
+        }
+        final FlowExecutionResult result;
+        try {
+            metadata.setRedirectionURI(new URI("https://example.org/cb"));
+            storeMetadataObject(storageService, clientId, clientSecret, metadata);
+            setBasicAuth("jdoe", "changeit");
+            result = flowExecutor.launchExecution(flowId, null, externalContext);
+            removeMetadata(storageService, clientId);
+        } catch (final IOException | URISyntaxException e) {
+            Assert.fail();
+            return;
+        }
+
+        final AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
+        final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+        Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
+        Assert.assertNull(successResponse.getIDToken());
+        Assert.assertNull(successResponse.getAccessToken());
+        Assert.assertNotNull(successResponse.getAuthorizationCode());
+        Assert.assertNull(successResponse.getIssuer());
+
+        final AuthorizeCodeClaimsSet code;
+        try {
+            code = AuthorizeCodeClaimsSet.parse(successResponse.getAuthorizationCode().getValue(), getDataSealer());
+        } catch (ParseException | DataSealerException e) {
+            Assert.fail("Could not create an authorize code", e);
+            return;
+        }
+        if (StringSupport.trimOrNull(requestObject) != null) {
+            Assert.assertNotNull(code.getClaimsRequest());
+            Assert.assertNotNull(code.getClaimsRequest().getIDTokenClaimsRequest());
+            Assert.assertNotNull(code.getClaimsRequest().getUserInfoClaimsRequest());
+            Assert.assertTrue(code.getClaimsRequest().getUserInfoClaimsRequest().getClaimNames(false)
+                    .contains("family_name"));
+            Assert.assertTrue(code.getClaimsRequest().getIDTokenClaimsRequest().getClaimNames(false)
+                    .contains("given_name"));
+            final ClaimsSetRequest.Entry familyName =
+                    code.getClaimsRequest().getUserInfoClaimsRequest().get("family_name", null);
+            Assert.assertEquals(familyName.getClaimName(), "family_name");
+            Assert.assertEquals(familyName.getClaimRequirement(), ClaimRequirement.ESSENTIAL);
+            final ClaimsSetRequest.Entry givenName =
+                    code.getClaimsRequest().getIDTokenClaimsRequest().get("given_name", null);
+            Assert.assertEquals(givenName.getClaimName(), "given_name");
+            Assert.assertEquals(givenName.getClaimRequirement(), ClaimRequirement.ESSENTIAL);
+        }
+        
+    }
+
+    protected void assertEncryptedSignedJwt(final JWT jwt, final JWSAlgorithm jwsAlg, final JWEAlgorithm jweAlg,
+            final EncryptionMethod method, final String clientSecret, final PrivateKey privateKey,
+            final PublicKey publicKey, final PublicKey jwsValidationKey) {
+        assertSuccessRequestObjectResponse(jwt.serialize(), jwsAlg, jweAlg, method, clientSecret, jwsValidationKey);
+    }
+
+    protected void assertSignedJwt(final JWT jwt, final JWSAlgorithm algorithm, final PublicKey publicKey,
+            final String clientSecret) {
+        assertSuccessRequestObjectResponse(jwt.serialize(), algorithm, null, null, clientSecret, publicKey);
+    }
+
+    protected JWT obtainRequestObject(final String clientSecret, final PublicKey publicKey,
+            final PrivateKey signingKey, final JWSAlgorithm storedJwsAlgorithm, final JWEAlgorithm storedJweAlgorithm,
+            final EncryptionMethod storedJweMethod) {
+        final String clientId = encryptionOptional ? defaultClientId : defaultClientIdEncryptionEnforced;
+        final String payload = AuthorizeFlowTest.getRequestObjectWithClaimsRequestPayload(clientId,
+                "https://example.org/cb");
+        final JWT jwt = processJwsForRequestObject(storedJwsAlgorithm, payload, clientSecret, signingKey);
+        
+        try {
+            if (storedJweAlgorithm != null) {
+                if (publicKey != null) {
+                    final BasicJWKCredential credential = new BasicJWKCredential();
+                    credential.setPublicKey(publicKey);
+                    return createEncryptedJWT(jwt.serialize(), storedJweAlgorithm, storedJweMethod, credential,
+                        clientSecret);
+                } else if (clientSecret != null) {
+                    return createEncryptedJWT(jwt.serialize(), storedJweAlgorithm, storedJweMethod, null, clientSecret,
+                            false);
+                }
+            }
+        } catch (JOSEException | ParseException e) {
+            Assert.fail("Could not encrypt the JWT", e);
+        }
+        return jwt;
+    }
+
+    protected JWT processJwsForRequestObject(final JWSAlgorithm storedJwsAlgorithm, final String payload,
+            final String clientSecret, final PrivateKey signingKey) {
+        try {
+            if (storedJwsAlgorithm == null) {
+                return new PlainJWT(JWTClaimsSet.parse(payload));
+            } else if (JWSAlgorithm.Family.EC.contains(storedJwsAlgorithm)) {
+                return createPrivateKeyJWT(JWTClaimsSet.parse(payload), (ECPrivateKey) signingKey, storedJwsAlgorithm);
+            } else if (JWSAlgorithm.Family.RSA.contains(storedJwsAlgorithm)) {
+                return createPrivateKeyJWT(JWTClaimsSet.parse(payload), (RSAPrivateKey) signingKey, storedJwsAlgorithm);
+            } else if (JWSAlgorithm.Family.HMAC_SHA.contains(storedJwsAlgorithm)) {
+                if (clientSecret == null) {
+                    return null;
+                }
+                return createSecretJWT(JWTClaimsSet.parse(payload), clientSecret, storedJwsAlgorithm);
+            }
+        } catch (JOSEException | ParseException e) {
+            Assert.fail(e.getMessage(), e);
+        }
+        return null;
+    }
+
+}
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RequestObjectJWSTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RequestObjectJWSTest.java
new file mode 100644
index 00000000..816bb3f1
--- /dev/null
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RequestObjectJWSTest.java
@@ -0,0 +1,241 @@
+/*
+ * 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.oidc.op.profile.flow;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.text.ParseException;
+import java.util.List;
+
+import org.springframework.webflow.executor.FlowExecutionResult;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.nimbusds.jose.EncryptionMethod;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jwt.JWT;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.PlainJWT;
+import com.nimbusds.oauth2.sdk.Scope;
+import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
+import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
+import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement;
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSetRequest;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
+
+import net.shibboleth.idp.plugin.oidc.op.token.support.AuthorizeCodeClaimsSet;
+import net.shibboleth.utilities.java.support.collection.Pair;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.utilities.java.support.security.DataSealerException;
+
+public class RequestObjectJWSTest extends IssuedSignedJWTTest {
+
+    String defaultClientIdSigningEnforced = "mockClientIdRequestObjectSigningEnforced";
+
+    private final boolean signingOptional;
+    
+    public RequestObjectJWSTest(final boolean optionalSigning) {
+        super(JWT_FETCHING_TYPE.REQUEST_OBJECT, AuthorizeFlowTest.FLOW_ID);
+        signingOptional = optionalSigning;
+    }
+
+    @Override @Test
+    public void testJwtSecurity_jwtSigAlgAndEncNotSpecified() throws Exception {
+        final JWT jwt = obtainJwt(null);
+        if (signingOptional) {
+            assertSuccessRequestObjectResponse(jwt.serialize(), null, defaultClientSecret64B, null);
+        } else {
+            assertErrorRequestObjectResponse(jwt.serialize(), null, defaultClientSecret64B, null);
+
+        }
+    }
+
+    @Override
+    protected JWT obtainJwt(final JWSAlgorithm jwsAlgorithm) {
+        return obtainJwt(defaultClientSecret64B, jwsAlgorithm);
+    }
+    
+    @Override
+    protected JWT obtainJwt(final String clientSecret, final JWSAlgorithm jwsAlgorithm) {
+        return obtainRequestObject(clientSecret, jwsAlgorithm);
+    }
+
+    @Override
+    protected void assertNoJwtResponse(final String clientId, final String clientSecret,
+            final PublicKey publicKey, final JWSAlgorithm jwsAlgorithm, final JWEAlgorithm jweAlgorithm,
+            final EncryptionMethod method, final JWT_FETCHING_TYPE fetchingType) {
+        final JWT jwt = obtainRequestObject(defaultClientSecret64B, jwsAlgorithm);
+        assertErrorRequestObjectResponse(jwt.serialize(), jwsAlgorithm, clientSecret, publicKey);
+    }
+
+    @Override
+    protected void assertExcludedAlgorithm(final String clientId, final String clientSecret, final PublicKey publicKey,
+            final JWSAlgorithm jwsAlgorithm) throws ParseException, DataSealerException, IOException {
+        final JWT jwt = obtainJwt(clientSecret, jwsAlgorithm);
+        assertErrorRequestObjectResponse(jwt.serialize(), jwsAlgorithm, clientSecret, publicKey);
+    }
+
+    protected void assertErrorRequestObjectResponse(final String requestObject,
+            final JWSAlgorithm requestObjectSigAlg, final String clientSecret, final PublicKey publicKey) {
+        request.setMethod("GET");
+        final String clientId = signingOptional ? defaultClientId : defaultClientIdSigningEnforced;
+        final String redirectUri = "https://example.org/cb";
+        AuthorizeFlowTest.setRequestParameters(request, List.of(new Pair<>("client_id", clientId),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("request", requestObject)));
+        initializeThreadLocals();
+
+        final OIDCClientMetadata metadata = buildMetadataSkeleton();
+        metadata.setScope(new Scope("openid"));
+        metadata.setUserInfoJWSAlg(requestObjectSigAlg);
+        if (publicKey != null) {
+            metadata.setJWKSet(super.buildJWKSet(publicKey));
+        }
+        try {
+            metadata.setRedirectionURI(new URI("https://example.org/cb"));
+            storeMetadataObject(storageService, clientId, clientSecret, metadata);
+            setBasicAuth("jdoe", "changeit");
+            final FlowExecutionResult result = flowExecutor.launchExecution(flowId, null, externalContext);
+            removeMetadata(storageService, clientId);
+            Assert.assertEquals(result.getOutcome().getId(), "ErrorView");        
+        } catch (final IOException | URISyntaxException e) {
+            Assert.fail();
+        }
+    }
+    
+    protected void assertSuccessRequestObjectResponse(final String requestObject,
+            final JWSAlgorithm requestObjectSigAlg, final String clientSecret, final PublicKey publicKey) {
+        request.setMethod("GET");
+        final String clientId = signingOptional ? defaultClientId : defaultClientIdSigningEnforced;
+        final String redirectUri = "https://example.org/cb";
+        AuthorizeFlowTest.setRequestParameters(request, List.of(new Pair<>("client_id", clientId),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("request", requestObject)));
+        initializeThreadLocals();
+
+        final OIDCClientMetadata metadata = buildMetadataSkeleton();
+        metadata.setScope(new Scope("openid"));
+        metadata.setUserInfoJWSAlg(requestObjectSigAlg);
+        if (publicKey != null) {
+            metadata.setJWKSet(super.buildJWKSet(publicKey));
+        }
+        final FlowExecutionResult result;
+        try {
+            metadata.setRedirectionURI(new URI("https://example.org/cb"));
+            storeMetadataObject(storageService, clientId, clientSecret, metadata);
+            setBasicAuth("jdoe", "changeit");
+            result = flowExecutor.launchExecution(flowId, null, externalContext);
+            removeMetadata(storageService, clientId);
+        } catch (final IOException | URISyntaxException e) {
+            Assert.fail();
+            return;
+        }
+
+        final AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
+        final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+        Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
+        Assert.assertNull(successResponse.getIDToken());
+        Assert.assertNull(successResponse.getAccessToken());
+        Assert.assertNotNull(successResponse.getAuthorizationCode());
+        Assert.assertNull(successResponse.getIssuer());
+
+        final AuthorizeCodeClaimsSet code;
+        try {
+            code = AuthorizeCodeClaimsSet.parse(successResponse.getAuthorizationCode().getValue(), getDataSealer());
+        } catch (ParseException | DataSealerException e) {
+            Assert.fail("Could not create an authorize code", e);
+            return;
+        }
+        if (StringSupport.trimOrNull(requestObject) != null) {
+            Assert.assertNotNull(code.getClaimsRequest());
+            Assert.assertNotNull(code.getClaimsRequest().getIDTokenClaimsRequest());
+            Assert.assertNotNull(code.getClaimsRequest().getUserInfoClaimsRequest());
+            Assert.assertTrue(code.getClaimsRequest().getUserInfoClaimsRequest().getClaimNames(false)
+                    .contains("family_name"));
+            Assert.assertTrue(code.getClaimsRequest().getIDTokenClaimsRequest().getClaimNames(false)
+                    .contains("given_name"));
+            final ClaimsSetRequest.Entry familyName =
+                    code.getClaimsRequest().getUserInfoClaimsRequest().get("family_name", null);
+            Assert.assertEquals(familyName.getClaimName(), "family_name");
+            Assert.assertEquals(familyName.getClaimRequirement(), ClaimRequirement.ESSENTIAL);
+            final ClaimsSetRequest.Entry givenName =
+                    code.getClaimsRequest().getIDTokenClaimsRequest().get("given_name", null);
+            Assert.assertEquals(givenName.getClaimName(), "given_name");
+            Assert.assertEquals(givenName.getClaimRequirement(), ClaimRequirement.ESSENTIAL);
+        }
+        
+    }
+
+    protected void assertSignedJwt(final JWT jwt, final JWSAlgorithm algorithm, final PublicKey publicKey,
+            final String clientSecret) {
+        assertSuccessRequestObjectResponse(jwt.serialize(), algorithm, clientSecret, publicKey);
+    }
+
+    protected JWT obtainRequestObject(final String clientSecret, final JWSAlgorithm storedJwsAlgorithm) {
+        final String clientId = signingOptional ? defaultClientId : defaultClientIdSigningEnforced;
+        final String payload = AuthorizeFlowTest.getRequestObjectWithClaimsRequestPayload(clientId,
+                "https://example.org/cb");
+
+        return processJwsForRequestObject(storedJwsAlgorithm, payload, clientSecret, getSigningKey(storedJwsAlgorithm));
+    }
+
+    protected static PrivateKey getSigningKey(final JWSAlgorithm jwsAlgorithm) {
+        if (JWSAlgorithm.ES256.equals(jwsAlgorithm)) {
+            return loadESSigningCredential().getPrivateKey();
+        } else if (JWSAlgorithm.ES384.equals(jwsAlgorithm)) {
+            return loadES384SigningCredential().getPrivateKey();
+        } else if (JWSAlgorithm.ES512.equals(jwsAlgorithm)) {
+            return loadES512SigningCredential().getPrivateKey();
+        }
+        return loadRSSigningCredential().getPrivateKey();
+    }
+
+    protected static JWT processJwsForRequestObject(final JWSAlgorithm storedJwsAlgorithm, final String payload,
+            final String clientSecret, final PrivateKey signingKey) {
+        try {
+            if (storedJwsAlgorithm == null) {
+                return new PlainJWT(JWTClaimsSet.parse(payload));
+            } else if (JWSAlgorithm.Family.EC.contains(storedJwsAlgorithm)) {
+                return createPrivateKeyJWT(JWTClaimsSet.parse(payload), (ECPrivateKey) signingKey, storedJwsAlgorithm);
+            } else if (JWSAlgorithm.Family.RSA.contains(storedJwsAlgorithm)) {
+                return createPrivateKeyJWT(JWTClaimsSet.parse(payload), (RSAPrivateKey) signingKey, storedJwsAlgorithm);
+            } else if (JWSAlgorithm.Family.HMAC_SHA.contains(storedJwsAlgorithm)) {
+                if (clientSecret == null) {
+                    return null;
+                }
+                return createSecretJWT(JWTClaimsSet.parse(payload), clientSecret, storedJwsAlgorithm);
+            }
+        } catch (JOSEException | ParseException e) {
+            Assert.fail(e.getMessage(), e);
+        }
+        return null;
+    }
+
+
+}
diff --git a/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml b/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml
index a7f08090..0af0bd12 100644
--- a/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml
+++ b/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml
@@ -80,6 +80,24 @@
                  </list>
             </property>
         </bean>
+        <bean parent="RelyingPartyByName" c:relyingPartyIds="mockClientIdRequestObjectEncryptionEnforced">
+            <property name="profileConfigurations">
+                 <list>
+                     <bean parent="OIDC.SSO.MDDriven" p:encryptionOptional="false" p:encryptRequestObject="true"/>
+                     <bean parent="OAUTH2.Token.MDDriven" p:encryptionOptional="false" />
+                     <bean parent="OIDC.UserInfo.MDDriven" p:encryptionOptional="false" />
+                 </list>
+            </property>
+        </bean>
+        <bean parent="RelyingPartyByName" c:relyingPartyIds="mockClientIdRequestObjectSigningEnforced">
+            <property name="profileConfigurations">
+                 <list>
+                     <bean parent="OIDC.SSO.MDDriven" p:encryptionOptional="true" p:useRequestObject="true" p:signRequestObject="true"/>
+                     <bean parent="OAUTH2.Token.MDDriven" p:encryptionOptional="false" />
+                     <bean parent="OIDC.UserInfo.MDDriven" p:encryptionOptional="false" />
+                 </list>
+            </property>
+        </bean>
         <bean parent="RelyingPartyByName" c:relyingPartyIds="mockClientIdRefreshTokenRotation">
             <property name="profileConfigurations">
                  <list>

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


More information about the commits mailing list