[java-idp-plugin-oidc-rp] branch main updated: Add request object signing support

Phil Smart philip.smart at jisc.ac.uk
Mon Jul 11 10:53:50 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=6eb003b09590f51719496771dea8640a09bda44e

The following commit(s) were added to refs/heads/main by this push:
     new 6eb003b  Add request object signing support
6eb003b is described below

commit 6eb003b09590f51719496771dea8640a09bda44e
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Mon Jul 11 11:53:44 2022 +0100

    Add request object signing support
    
    Cleanup flow predicates
    fix tests
---
 .../oidc/rp/config/logic/IsCodeFlowPredicate.java  |  56 ++++++++
 .../rp/config/logic/IsHybridFlowPredicate.java     |  56 ++++++++
 .../rp/config/logic/IsImplicitFlowPredicate.java   |  56 ++++++++
 .../config/logic/SignRequestObjectPredicate.java   |  45 +++++++
 .../OutboundMessageContextFromProxyPRC.java        |  55 ++++++++
 .../rp/context/OutboundMessageHandlerContext.java  | 119 +++++++++++++++++
 .../logic/RequestObjectRequiredAndSupported.java   |  12 +-
 .../oidc/rp/impl/AuthorizationController.java      |  55 ++------
 .../authn/oidc/rp/impl/BuildRequestObject.java     |  99 ++++++++++++--
 .../impl/DefaultRedirectUriCreationFunction.java   |   4 +-
 .../authn/oidc/rp/impl/OIDCProxySupport.java       |   8 +-
 ...actOIDCAuthenticationRequestMessageHandler.java | 133 +++++++++++++++++++
 .../oidc/rp/messaging/impl/AddRedirectURI.java     | 111 ++++++++++++++++
 .../authn/oidc/rp/messaging/impl/AddState.java     | 108 ++++++++++++++++
 .../messaging/impl/BuildPlainRequestObjectJWT.java |  58 +++++++++
 .../rp/{ => messaging}/impl/SignRequestObject.java | 124 ++++++++++++------
 .../META-INF/net.shibboleth.idp/postconfig.xml     |  39 ++++--
 .../oidc-relying-party-authn-beans.xml             | 121 +++++++++--------
 .../oidc-relying-party-authn-flow.xml              |  80 +++++-------
 .../oidc/rp/impl/AuthorizationControllerTest.java  | 143 +++++++++++++++++++--
 .../plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java  |  92 +++----------
 .../authn/oidc/rp/impl/TestCredentialHelper.java   |  86 +++++++++++++
 .../resources/conf/test-relying-party-system.xml   |   2 +-
 23 files changed, 1364 insertions(+), 298 deletions(-)

diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsCodeFlowPredicate.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsCodeFlowPredicate.java
new file mode 100644
index 0000000..72d2295
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsCodeFlowPredicate.java
@@ -0,0 +1,56 @@
+/*
+ * 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.config.logic;
+
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+
+/** 
+ * A predicate that determines if the flow is a 'code-flow' using the AuthenticationRequest that is inside
+ * the message context.
+ */
+public class IsCodeFlowPredicate implements Predicate<MessageContext> {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(IsCodeFlowPredicate.class);
+
+    @Override
+    public boolean test(@Nullable final MessageContext input) {
+        if (input == null) {
+            log.trace("Message context was null, can not determine flow type");
+            return false;
+        }
+        if (!(input.getMessage() instanceof OIDCAuthenticationRequest)) {
+            log.trace("Message context did not contain an authentication request, can not determine flow type");
+            return false;
+        }
+        if (((OIDCAuthenticationRequest)input.getMessage()).getResponseType() == null) {
+            log.trace("Authentication request did not contain a response_type, can not determine flow type");
+            return false;
+        }
+        return ((OIDCAuthenticationRequest)input.getMessage()).getResponseType().impliesCodeFlow();
+    }
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsHybridFlowPredicate.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsHybridFlowPredicate.java
new file mode 100644
index 0000000..d43b0ec
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsHybridFlowPredicate.java
@@ -0,0 +1,56 @@
+/*
+ * 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.config.logic;
+
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+
+/** 
+ * A predicate that determines if the flow is a 'hybrid-flow' using the AuthenticationRequest that is inside
+ * the message context.
+ */
+public class IsHybridFlowPredicate implements Predicate<MessageContext> {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(IsHybridFlowPredicate.class);
+
+    @Override
+    public boolean test(@Nullable final MessageContext input) {
+        if (input == null) {
+            log.trace("Message context was null, can not determine flow type");
+            return false;
+        }
+        if (!(input.getMessage() instanceof OIDCAuthenticationRequest)) {
+            log.trace("Message context did not contain an authentication request, can not determine flow type");
+            return false;
+        }
+        if (((OIDCAuthenticationRequest)input.getMessage()).getResponseType() == null) {
+            log.trace("Authentication request did not contain a response_type, can not determine flow type");
+            return false;
+        }
+        return ((OIDCAuthenticationRequest)input.getMessage()).getResponseType().impliesHybridFlow();
+    }
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsImplicitFlowPredicate.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsImplicitFlowPredicate.java
new file mode 100644
index 0000000..ed40d87
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsImplicitFlowPredicate.java
@@ -0,0 +1,56 @@
+/*
+ * 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.config.logic;
+
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+
+/** 
+ * A predicate that determines if the flow is a 'implicit-flow' using the AuthenticationRequest that is inside
+ * the message context.
+ */
+public class IsImplicitFlowPredicate implements Predicate<MessageContext> {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(IsImplicitFlowPredicate.class);
+
+    @Override
+    public boolean test(@Nullable final MessageContext input) {
+        if (input == null) {
+            log.trace("Message context was null, can not determine flow type");
+            return false;
+        }
+        if (!(input.getMessage() instanceof OIDCAuthenticationRequest)) {
+            log.trace("Message context did not contain an authentication request, can not determine flow type");
+            return false;
+        }
+        if (((OIDCAuthenticationRequest)input.getMessage()).getResponseType() == null) {
+            log.trace("Authentication request did not contain a response_type, can not determine flow type");
+            return false;
+        }
+        return ((OIDCAuthenticationRequest)input.getMessage()).getResponseType().impliesImplicitFlow();
+    }
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/SignRequestObjectPredicate.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/SignRequestObjectPredicate.java
new file mode 100644
index 0000000..cb48477
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/SignRequestObjectPredicate.java
@@ -0,0 +1,45 @@
+/*
+ * 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.config.logic;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.profile.config.ProfileConfiguration;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.logic.AbstractRelyingPartyPredicate;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+
+/** A predicate that determines if the RequestObject JWT should be signed based on the profile configuration.*/
+public class SignRequestObjectPredicate extends AbstractRelyingPartyPredicate {
+
+    @Override
+    public boolean test(@Nullable final ProfileRequestContext input) {
+        
+        final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
+        if (rpc != null) {
+            final ProfileConfiguration pc = rpc.getProfileConfig();
+            if (pc instanceof OIDCAuthorizationConfiguration) {
+                return ((OIDCAuthorizationConfiguration) pc).isSignRequestObject(input);
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/OutboundMessageContextFromProxyPRC.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/OutboundMessageContextFromProxyPRC.java
new file mode 100644
index 0000000..6c942bc
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/OutboundMessageContextFromProxyPRC.java
@@ -0,0 +1,55 @@
+/*
+ * 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.config.navigate;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+
+/**
+ * A {@link ContextDataLookupFunction} that returns the outbound {@link MessageContext} for a
+ * {@link ProfileRequestContext} located inside an {@link AuthenticationContext} i.e. for use in the proxy case.
+ */
+public class OutboundMessageContextFromProxyPRC
+        implements ContextDataLookupFunction<ProfileRequestContext, MessageContext> {
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public MessageContext apply(@Nullable final ProfileRequestContext input) {
+        
+        if (input == null) {
+            return null;
+        }
+        final AuthenticationContext authContext = input.getSubcontext(AuthenticationContext.class);
+        if (authContext == null) {
+            return null;
+        }
+        final ProfileRequestContext nestedProfileRequestContext = 
+                authContext.getSubcontext(ProfileRequestContext.class);        
+        if (nestedProfileRequestContext != null) {
+            return nestedProfileRequestContext.getOutboundMessageContext();
+        }
+        return null;
+    }
+
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OutboundMessageHandlerContext.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OutboundMessageHandlerContext.java
new file mode 100644
index 0000000..562c001
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OutboundMessageHandlerContext.java
@@ -0,0 +1,119 @@
+/*
+ * 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.context;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.opensaml.messaging.context.BaseContext;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/** 
+ * A context to stash HTTP request/response controller parameters for use by message handlers.
+ * For example, the OIDC RP preEncodeMessageHandlers.
+ */
+public class OutboundMessageHandlerContext extends BaseContext {
+    
+    /** The Http servlet request.*/
+    @Nullable private HttpServletRequest servletRequest;
+    
+    /** The Http servlet response.*/
+    @Nullable private HttpServletResponse servletResponse;
+    
+    /** The spring webflow key.*/
+    @Nullable private String webflowKey;
+
+    /**
+     * Constructor.
+     *
+     * @param request the servlet request
+     * @param response the servlet response
+     * @param key the swf key
+     */
+    public OutboundMessageHandlerContext(@Nonnull final HttpServletRequest request, 
+            @Nonnull final HttpServletResponse response, @Nonnull final String key) {
+        super();
+        servletRequest = Constraint.isNotNull(request, "Http Servlet Request can not be null");
+        servletResponse = Constraint.isNotNull(response, "Http Servlet Response can not be null");
+        webflowKey = Constraint.isNotNull(key, "Spring Webflow Key can not be null");
+    }
+    
+    /** Constructor to allow no-arg construction.*/
+    public OutboundMessageHandlerContext() {
+        // Do nothing
+    }
+    
+    /**
+     * Set the Http servlet request.
+     * @param request
+     */
+    public void setServletRequest(@Nonnull final HttpServletRequest request) {
+        servletRequest = Constraint.isNotNull(request, "Http Servlet Request can not be null");
+    }
+
+    /**
+     * Get the Http servlet request.
+     * 
+     * @return Returns the servletRequest.
+     */
+    @Nullable public HttpServletRequest getServletRequest() {
+        return servletRequest;
+    }
+    
+    /**
+     * Set the Http servlet response.
+     * 
+     * @param response the servlet response
+     */
+    public void setServletResponse(@Nonnull final HttpServletResponse response) {
+        servletResponse = Constraint.isNotNull(response, "Http Servlet Response can not be null");
+    }
+
+    /**
+     * Get the Http servlet response.
+     * 
+     * @return Returns the servletResponse.
+     */
+    @Nullable public HttpServletResponse getServletResponse() {
+        return servletResponse;
+    }
+    
+    /**
+     * Set the Spring Webflow execution key.
+     *  
+     * @param key the swf execution key
+     */
+    public void setWebflowKey(@Nonnull final String key) {
+        webflowKey = Constraint.isNotNull(key, "Spring Webflow Key can not be null");
+    }
+
+    /**
+     * Get the Spring Webflow execution key.
+     * 
+     * @return Returns the webflowKey.
+     */
+    @Nullable public String getWebflowKey() {
+        return webflowKey;
+    }
+    
+    
+
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/RequestObjectRequiredAndSupported.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/RequestObjectRequiredAndSupported.java
index 3246ed0..49e2f59 100644
--- a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/RequestObjectRequiredAndSupported.java
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/RequestObjectRequiredAndSupported.java
@@ -83,14 +83,14 @@ public class RequestObjectRequiredAndSupported extends AbstractRelyingPartyPredi
             return false;
         }
 
+        final boolean isSupportedByOP = metadata.getProviderInformation().supportsRequestParam();
+        
         final boolean requestedAndSupport =
-                requestObjectRequestedFromConfig && metadata.getProviderInformation().supportsRequestParam(); 
-        if (requestedAndSupport) {
-            log.debug("Authentication request RequestObject was requested and is supported");
-        } else {
-            log.debug("Authentication request RequestObject not requested or not supported");
-        }
+                requestObjectRequestedFromConfig && isSupportedByOP; 
         
+        log.debug("Authentication RequestObject was requested '{}', is supported by the OP '{}', "
+                + "will be used '{}'", requestObjectRequestedFromConfig, isSupportedByOP, requestedAndSupport);
+       
         return requestedAndSupport;
 
     }
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java
index b57c363..37ac136 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java
@@ -53,6 +53,7 @@ import net.shibboleth.idp.plugin.authn.oidc.rp.OIDCProxyException;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCAuthnContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OutboundMessageHandlerContext;
 import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
@@ -100,11 +101,8 @@ public class AuthorizationController extends AbstractInitializableComponent {
     @Nonnull private final Logger log = LoggerFactory.getLogger(AuthorizationController.class);
     
     /** Lookup strategy to locate the nested ProfileRequestContext. */
-    @Nonnull private Function<ProfileRequestContext,ProfileRequestContext> profileRequestContextLookupStrategy;
-    
-    /** Function to create a suitable redirect URI from the given servlet request and profile request context.*/
-    @NonnullAfterInit private BiFunction<HttpServletRequest, ProfileRequestContext, URI> redirectUriCreationStrategy;
-    
+    @Nonnull private Function<ProfileRequestContext,ProfileRequestContext> profileRequestContextLookupStrategy; 
+
     /** Lookup strategy to locate the SAML context. */
     @Nonnull private Function<ProfileRequestContext,OIDCAuthnContext> oidcContextLookupStrategy;
     
@@ -168,31 +166,9 @@ public class AuthorizationController extends AbstractInitializableComponent {
         ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
         
         oidcContextLookupStrategy = Constraint.isNotNull(strategy, "OIDCAuthnContext lookup strategy cannot be null");
-    }
-    
-    /**
-     * Set the creation strategy used to compute or lookup a redirect URI.
-     * 
-     * @param strategy the creation strategy
-     */
-    public void setRedirectUriCreationStrategy(
-            @Nonnull final BiFunction<HttpServletRequest, ProfileRequestContext, URI> strategy) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+    }    
+       
         
-        redirectUriCreationStrategy = 
-                Constraint.isNotNull(strategy, "RedirectURI creation lookup strategy cannot be null");
-    }
-    
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-        
-        if (redirectUriCreationStrategy == null) {
-            throw new ComponentInitializationException("redirectUriCreationStrategy cannot be null");
-        }
-    }
-
     /**
      * Begin an authorization request to the configured downstream OP. 
      * 
@@ -229,20 +205,13 @@ public class AuthorizationController extends AbstractInitializableComponent {
         
         if (nestedPRC.getOutboundMessageContext() != null &&
                 nestedPRC.getOutboundMessageContext().getMessage() instanceof OIDCAuthenticationRequest) {
-            // Add key and nonce to state
-            final String state = OIDCProxySupport.generateState(OIDCProxySupport.generateNonce(32), key);
-            ((OIDCAuthenticationRequest)nestedPRC.getOutboundMessageContext().getMessage()).setState(new State(state));
             
-            //add redirect URI based on the request URL and any found in the OIDCAuthnContext
-            final URI redirectUri = redirectUriCreationStrategy.apply(httpRequest, nestedPRC);
-            if (redirectUri == null) {
-                log.error("Redirect URI could not be located or created from strategy");
-                httpRequest.setAttribute(ExternalAuthentication.AUTHENTICATION_ERROR_KEY, EventIds.INVALID_PROFILE_CTX);
-                ExternalAuthentication.finishExternalAuthentication(key, httpRequest, httpResponse);
-                return;
-            }
-            log.trace("Created redirect_uri '{}'", redirectUri);
-            ((OIDCAuthenticationRequest)nestedPRC.getOutboundMessageContext().getMessage()).setRedirectURI(redirectUri);
+            // Build a handler context to allow certain parameters to be set e.g. state, redirect_uri, 
+            // by the pre-message-encoders.
+            final OutboundMessageHandlerContext handlerContext = new OutboundMessageHandlerContext(httpRequest,
+                    httpResponse, key);
+            
+            nestedPRC.getOutboundMessageContext().addSubcontext(handlerContext);                    
             
         } else {
             log.error("Outbound Authorization message not found");
@@ -258,7 +227,7 @@ public class AuthorizationController extends AbstractInitializableComponent {
             
             oidcContext.getEncodeMessageAction().execute(nestedPRC);
             // Handle error added by the EncodeMessage action. 
-            final EventContext eventCtx = prc.getSubcontext(EventContext.class);
+            final EventContext eventCtx = nestedPRC.getSubcontext(EventContext.class);
             if (eventCtx != null && eventCtx.getEvent() != null
                     && !EventIds.PROCEED_EVENT_ID.equals(eventCtx.getEvent())) {
                 log.error("Message encoding action signaled non-proceed event {}", eventCtx.getEvent());
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 8fb8afd..7402645 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
@@ -1,5 +1,24 @@
+/*
+ * 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.Predicate;
+
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
@@ -8,9 +27,8 @@ import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Predicates;
 import com.nimbusds.jwt.JWT;
-import com.nimbusds.jwt.PlainJWT;
-import com.nimbusds.oauth2.sdk.ParseException;
 import com.nimbusds.oauth2.sdk.id.Audience;
 import com.nimbusds.oauth2.sdk.id.Issuer;
 import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
@@ -19,31 +37,60 @@ import net.shibboleth.idp.authn.AuthnEventIds;
 import net.shibboleth.idp.authn.context.AuthenticationContext;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
 
 
 /**
  * Action that creates a Request Object {@link JWT} object, and sets it to work context
  * {@link OIDCAuthenticationRequest} located under {@link ProfileRequestContext#getOutboundMessageContext()}.
+ * 
+ * <p>Note, some parameters are set downstream before the request object is signed and or encrypted. These
+ * parameters are only available to the Http Controller e.g. state and hostname, and must be set during the external
+ * authentication redirect.</p>
  */
 public class BuildRequestObject extends AbstractOIDCAuthenticationRequestAction {
     
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(BuildRequestObject.class);
     
+    /** A hook to allow additional checking of the request object claims after it is build.*/
+    @Nonnull private Predicate<ClaimsSet> claimsSetIsValidPredicate;
+    
     /** The RelyingPartyContext to operate on. */
     @Nullable private RelyingPartyContext rpCtx;
+    
+    /** Constructor.*/
+    public BuildRequestObject() {
+        claimsSetIsValidPredicate = Predicates.alwaysTrue();
+    }
+    
+    /**
+     * Set a hook that allows the built request object to be validated before it is used.
+     * This is run in addition too, but before, the built in validation taken from the specification.
+     * If this returns false, the built in validation is not run, and validation fails.
+     * 
+     * @param predicate the hook to run
+     */
+    public void setClaimsSetIsValidPredicate(@Nullable final Predicate<ClaimsSet> predicate) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+        
+        if (predicate != null) {
+            claimsSetIsValidPredicate = predicate;
+        }
+    }
 
     /** {@inheritDoc} */
     @Override
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
             @Nonnull final AuthenticationContext authenticationContext) {
         
-        log.debug("{} Building a Request Object JWT for the authentication request", getLogPrefix());
+        log.debug("{} Building a plain RequestObject JWT", getLogPrefix());
         final ClaimsSet requestObjectClaims = new ClaimsSet();     
         
         //TODO check we are signing if we have selected a signing params, or if we just say must sign?
         
-        requestObjectClaims.setClaim("client_id", getAuthenticationRequest().getClientID().getValue());
+        requestObjectClaims.setClaim("client_id", getAuthenticationRequest().getClientID());
         requestObjectClaims.setAudience(
                 new Audience(getProviderMetadataContext().getProviderInformation().getIssuer().getValue()));
         requestObjectClaims.setIssuer(new Issuer(getAuthenticationRequest().getClientID().getValue())); 
@@ -59,18 +106,48 @@ public class BuildRequestObject extends AbstractOIDCAuthenticationRequestAction
         
         requestObjectClaims.setClaim("scope", getAuthenticationRequest().getScope());
         
+        // ACRs?
+        
+        // Validate the request object
+        if (!validateRequestObject(requestObjectClaims)) {
+            log.error("{} RequestObject claims are not valid", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_AUTHN_CTX);
+            return;
+        }
+        
         if (log.isDebugEnabled()) {
-            log.debug("{} Setting request object response claims to authentication context {}", getLogPrefix(),
+            log.debug("{} Setting the RequestObject claims: {}", getLogPrefix(),
                     requestObjectClaims.toJSONString());
         }
         
-        // Create a plain JWT at first, can be signed and encrypted later
-        try {
-            getAuthenticationRequest().setRequestObject(new PlainJWT(requestObjectClaims.toJWTClaimsSet()));
-        } catch (final ParseException e) {
-            log.error("{} Unable to set request object claims as JWT claims", getLogPrefix(), e);
-            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_AUTHN_CTX);
+        // Create a plain JWT at first, can be signed and encrypted later        
+        getAuthenticationRequest().setRequestObjectClaimsSet(requestObjectClaims);
+               
+    }
+    
+    /**
+     * Ensure the request object is valid by assessing the claims are correct.
+     * 
+     * @param requestObjectClaims the claims of the request object
+     * 
+     * @return true if the request object claims are valid, false otherwise
+     */
+    private boolean validateRequestObject(final ClaimsSet requestObjectClaims) {
+        
+        if (!claimsSetIsValidPredicate.test(requestObjectClaims)) {
+            return false;
+        }
+        // TODO only if signing is enabled
+        if (true) {
+            if (requestObjectClaims.getClaim("iss") == null) {
+                return false;
+            }
+            if (requestObjectClaims.getClaim("aud") == null) {
+                return false;
+            }
+            
         }
+        return true;
     }
 
 }
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DefaultRedirectUriCreationFunction.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DefaultRedirectUriCreationFunction.java
index b67d742..36244bf 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DefaultRedirectUriCreationFunction.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DefaultRedirectUriCreationFunction.java
@@ -68,7 +68,7 @@ public class DefaultRedirectUriCreationFunction
     /** The strategy used to lookup or create the {@link OAuth2ClientContext} for storing the client_id.*/
     @Nonnull private Function<ProfileRequestContext, OAuth2ClientContext> oauth2ClientContextLookupStrategy;
     
-    /** The path, excluding the context and servlet paths, to the Duo callback handler.*/
+    /** The path, excluding the context and servlet paths, to the RP callback handler.*/
     @Nonnull @NotEmpty private final String callbackServletPath;
     
     /** 
@@ -87,7 +87,7 @@ public class DefaultRedirectUriCreationFunction
             @Nonnull @NotEmpty @ParameterName(name="callbackPath") final String callbackPath,
             @Nonnull @NotEmpty @ParameterName(name="allowedOrigins") @Nullable final Set<String> origins) {
         
-        callbackServletPath = Constraint.isNotNull(callbackPath,"Duo Call back path can not be null");
+        callbackServletPath = Constraint.isNotNull(callbackPath,"RP Proxy Call back path can not be null");
         // Default under OIDCPeerEntityContext in the outbound context (create true) under the nested PRC.
         oauth2ClientContextLookupStrategy = new ChildContextLookup<>(OAuth2ClientContext.class).compose(
                 new ChildContextLookup<>(OIDCPeerEntityContext.class).compose(
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCProxySupport.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCProxySupport.java
index c888feb..8028995 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCProxySupport.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCProxySupport.java
@@ -45,7 +45,7 @@ public final class OIDCProxySupport {
      * 
      * @return the randomly generated nonce value.
      */
-    @Nonnull static String generateNonce(@Nonnull final Integer length) {
+    @Nonnull public static String generateNonce(@Nonnull final Integer length) {
         final SecureRandom secureRandom = new SecureRandom();
         final StringBuilder sb = new StringBuilder();
         while(sb.length() < length){
@@ -69,7 +69,7 @@ public final class OIDCProxySupport {
      * 
      * @return the combined state component.
      */
-    @Nonnull static String generateState(@Nonnull final String nonce, @Nonnull final String key) {
+    @Nonnull public static String generateState(@Nonnull final String nonce, @Nonnull final String key) {
         Constraint.isNotNull(nonce, "NonceHex key can not be null");
         Constraint.isNotNull(key, "Webflow execution key can not be null");
         
@@ -87,7 +87,7 @@ public final class OIDCProxySupport {
      * 
      * @throws OIDCProxyException if the key component can not be found, or hex decoding fails.
      */
-    @Nonnull static String extractKeyFromState(@Nonnull final String state) throws OIDCProxyException {
+    @Nonnull public static String extractKeyFromState(@Nonnull final String state) throws OIDCProxyException {
         Constraint.isNotNull(state, "State can not be null");
         
         final String[] stateSplit = state.split("\\.");
@@ -114,7 +114,7 @@ public final class OIDCProxySupport {
      * 
      * @throws DuoException if the nonce component can not be found.
      */
-    @Nonnull static String extractNonceFromState(@Nonnull final String state) throws OIDCProxyException {
+    @Nonnull public static String extractNonceFromState(@Nonnull final String state) throws OIDCProxyException {
         Constraint.isNotNull(state, "State can not be null");
 
         final String[] stateSplit = state.split("\\.");
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AbstractOIDCAuthenticationRequestMessageHandler.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AbstractOIDCAuthenticationRequestMessageHandler.java
new file mode 100644
index 0000000..d56dab5
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AbstractOIDCAuthenticationRequestMessageHandler.java
@@ -0,0 +1,133 @@
+/*
+ * 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.messaging.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.handler.AbstractMessageHandler;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OutboundMessageHandlerContext;
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/** An abstract message handler that pulls out the {@link OutboundMessageHandlerContext} from the message context.*/
+public abstract class AbstractOIDCAuthenticationRequestMessageHandler extends AbstractMessageHandler {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractOIDCAuthenticationRequestMessageHandler.class);
+    
+    /** Strategy to locate the {@link OutboundMessageHandlerContext}.*/
+    @Nonnull 
+    private Function<MessageContext, OutboundMessageHandlerContext> outboundMessageHandlerContextLookupStrategy;
+    
+    /** Strategy used to locate the {@link OIDCAuthenticationRequest} to sign.  */
+    @Nonnull private Function<MessageContext, OIDCAuthenticationRequest> authenticationRequestLookupStrategy;
+    
+    /** The stashed {@link OutboundMessageHandlerContext}.*/
+    @Nullable private OutboundMessageHandlerContext outboundMessageContext;
+    
+    /** The stashed {@link OIDCAuthenticationRequest}.*/
+    @Nullable private OIDCAuthenticationRequest authnRequest;
+    
+    /** Constructor.*/
+    protected AbstractOIDCAuthenticationRequestMessageHandler() {
+        outboundMessageHandlerContextLookupStrategy = new ChildContextLookup<>(OutboundMessageHandlerContext.class);
+        
+        authenticationRequestLookupStrategy = mc -> {
+            if (mc.getMessage() instanceof OIDCAuthenticationRequest) {
+                return (OIDCAuthenticationRequest)mc.getMessage();
+            }
+            return null;
+        };
+    }
+    
+
+    /**
+     * Set the strategy used to locate the {@link OIDCAuthenticationRequest} to use.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setAuthenticationRequestLookupStrategy(
+            @Nonnull final Function<MessageContext, OIDCAuthenticationRequest> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        authenticationRequestLookupStrategy =
+                Constraint.isNotNull(strategy, "AuthenticationRequestLookupStrategy lookup strategy cannot be null");
+    }
+    
+    /**
+     * Set the strategy used to locate the {@link OutboundMessageHandlerContext}.
+     * 
+     * @param strategy the strategy
+     */
+    public void setOutboundMessageHandlerContextLookupStrategy(
+            @Nonnull final Function<MessageContext, OutboundMessageHandlerContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        outboundMessageHandlerContextLookupStrategy = 
+                Constraint.isNotNull(strategy, "outboundMessageHandlerContextLookupStrategy can not be null");
+    }
+    
+    @Override
+    protected boolean doPreInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+        outboundMessageContext = outboundMessageHandlerContextLookupStrategy.apply(messageContext);
+        if (outboundMessageContext == null) {
+            log.debug("{} Outbound message context is null", getLogPrefix());
+            throw new MessageHandlerException("Outbound message context is null");
+        }
+        
+        authnRequest = authenticationRequestLookupStrategy.apply(messageContext);
+        if (authnRequest == null) {
+            log.debug("{} OIDC authentication request is null", getLogPrefix());
+            throw new MessageHandlerException("OIDC authentication request is null");
+        }
+        
+        return super.doPreInvoke(messageContext);
+    }
+    
+    /**
+     * Get the outbound message context.
+     * 
+     * @return the outbound message context.
+     */
+    @Nullable protected OutboundMessageHandlerContext getOutboundMessageContext() {
+        return outboundMessageContext;
+    }
+    
+    /**
+     * Get the authentication request.
+     * 
+     * @return the authentication request
+     */
+    @Nullable protected OIDCAuthenticationRequest getAuthenticationRequest() {
+        return authnRequest;
+    }
+    
+    
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AddRedirectURI.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AddRedirectURI.java
new file mode 100644
index 0000000..d8c7cca
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AddRedirectURI.java
@@ -0,0 +1,111 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl;
+
+import java.net.URI;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.context.navigate.RecursiveTypedParentContextLookup;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
+
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/** Adds a redirect_uri to the authentication request URL and the request object claims (if present).*/
+public class AddRedirectURI extends AbstractOIDCAuthenticationRequestMessageHandler {
+    
+    /** The name of the redirect uri claim.*/
+    private static final String REDIRECT_URI_CLAIM = "redirect_uri";
+
+    /** Logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(AddRedirectURI.class);
+    
+    /** Function to create a suitable redirect URI from the given servlet request and profile request context.*/
+    @NonnullAfterInit private BiFunction<HttpServletRequest, ProfileRequestContext, URI> redirectUriCreationStrategy;
+    
+    /** Locat the profile request context from the given message context. */
+    @Nonnull private Function<MessageContext, ProfileRequestContext> locateProfileRequestContextStrategy;
+    
+    /** Constructor.*/
+    public AddRedirectURI() {
+        locateProfileRequestContextStrategy = new RecursiveTypedParentContextLookup<>(ProfileRequestContext.class);
+    }
+    
+    /**
+     * Set the strategy used to locate the profile request context from the message context.
+     * 
+     * @param strategy the strategy.
+     */
+    public void setLocateProfileRequestContextStrategy(
+            final Function<MessageContext, ProfileRequestContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+        
+        locateProfileRequestContextStrategy = 
+                Constraint.isNotNull(strategy, "LocateProfileRequestStrategy cannot be null");
+    }
+    
+    /**
+     * Set the creation strategy used to compute or lookup a redirect URI.
+     * 
+     * @param strategy the creation strategy
+     */
+    public void setRedirectUriCreationStrategy(
+            @Nullable final BiFunction<HttpServletRequest, ProfileRequestContext, URI> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+        
+        if (strategy != null) {
+            redirectUriCreationStrategy = 
+                Constraint.isNotNull(strategy, "RedirectURI creation lookup strategy cannot be null");
+        }
+    }
+    
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (redirectUriCreationStrategy == null) {
+            throw new ComponentInitializationException("redirectUriCreationStrategy cannot be null");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+        
+        final ProfileRequestContext prc = locateProfileRequestContextStrategy.apply(messageContext);
+        if (prc == null) {
+            throw new MessageHandlerException(
+                    "Unable to locate the profile request context, redirect_uri can not be determined");
+        }
+        
+        final URI redirectUri = 
+                redirectUriCreationStrategy.apply(getOutboundMessageContext().getServletRequest(), prc);
+        if (redirectUri == null) {
+            throw new MessageHandlerException("Redirect URI could not be located or created from strategy");
+        }
+        log.trace("Created redirect_uri '{}'", redirectUri);
+        getAuthenticationRequest().setRedirectURI(redirectUri);
+        
+        // Also add to request object if exists
+        if (getAuthenticationRequest().getRequestObjectClaimsSet() != null) {            
+            final ClaimsSet claims = getAuthenticationRequest().getRequestObjectClaimsSet();
+            log.trace("{} Adding redirect_uri to JWT RequestObject", getLogPrefix());
+            claims.setClaim(REDIRECT_URI_CLAIM, redirectUri);           
+        } 
+         
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AddState.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AddState.java
new file mode 100644
index 0000000..579c441
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AddState.java
@@ -0,0 +1,108 @@
+/*
+ * 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.messaging.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.id.State;
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
+
+import net.shibboleth.idp.plugin.authn.oidc.rp.impl.OIDCProxySupport;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+
+/** Add state to the authentication request URL and the request object claims (if present) .*/
+public class AddState extends AbstractOIDCAuthenticationRequestMessageHandler {
+    
+    /** The 'state' claim name.*/
+    @Nonnull private static final String STATE_CLAIM = "state";
+
+    /** Logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(AddState.class);
+    
+    /** Function to create a suitable 'state' to add to the authentication request.*/
+    @Nonnull private Function<MessageContext, String> stateGenerationStrategy;
+    
+    /** Constructor.*/
+    public AddState() {
+        // By default, generate state from the SWF key and a 32 character nonce.
+        stateGenerationStrategy = msg -> {
+            if (getOutboundMessageContext() != null) {
+                return OIDCProxySupport.generateState(OIDCProxySupport.generateNonce(32), 
+                        getOutboundMessageContext().getWebflowKey());
+            } 
+            return null;
+        };
+    }
+    
+    /**
+     * Set the creation strategy used to compute the 'state'.
+     * 
+     * @param strategy the creation strategy
+     */
+    public void setStateGenerationStrategy(
+            @Nullable final  Function<MessageContext, String> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+        
+        if (strategy != null) {
+            stateGenerationStrategy = strategy;
+        }
+        
+    }
+    
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (stateGenerationStrategy == null) {
+            throw new ComponentInitializationException("redirectUriCreationStrategy cannot be null");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+        
+        final String stateString = stateGenerationStrategy.apply(messageContext);
+        log.trace("{} Generated state '{}'", getLogPrefix(), stateString);
+        if (stateString == null) {
+            throw new MessageHandlerException("Generated state was null");
+        }
+        final State state = new State(stateString);
+        
+        // Add to outer request
+        getAuthenticationRequest().setState(state);
+        
+        // Add to Request Object if exists
+        if (getAuthenticationRequest().getRequestObjectClaimsSet() != null) {            
+            final ClaimsSet claims = getAuthenticationRequest().getRequestObjectClaimsSet();
+            log.trace("{} Adding state to JWT RequestObject", getLogPrefix());
+            claims.setClaim(STATE_CLAIM, state);           
+        }               
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/BuildPlainRequestObjectJWT.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/BuildPlainRequestObjectJWT.java
new file mode 100644
index 0000000..df2de95
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/BuildPlainRequestObjectJWT.java
@@ -0,0 +1,58 @@
+/*
+ * 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.messaging.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jwt.PlainJWT;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
+
+/** 
+ * If the Request Object claims are present in the authentication request, convert them 
+ * into a JWTClaimsSet inside a PlainJWT. 
+ */
+public class BuildPlainRequestObjectJWT extends AbstractOIDCAuthenticationRequestMessageHandler {
+
+    /** Logger. */
+    @Nonnull
+    private final Logger log = LoggerFactory.getLogger(BuildPlainRequestObjectJWT.class);
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+        final ClaimsSet requestObjectClaims = getAuthenticationRequest().getRequestObjectClaimsSet();
+        if (requestObjectClaims == null) {
+            log.trace("{} RequestObject claims are not present, request object JWT skipped", getLogPrefix());
+            return;
+        }
+        try {
+            getAuthenticationRequest().setRequestObject(new PlainJWT(requestObjectClaims.toJWTClaimsSet()));
+            log.trace("{} Built Plain JWT RequestObject from claims", getLogPrefix());
+        } catch (final ParseException e) {
+            throw new MessageHandlerException("Unable to generate request object JWT", e);
+        }
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SignRequestObject.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/SignRequestObject.java
similarity index 62%
rename from idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SignRequestObject.java
rename to idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/SignRequestObject.java
index 797821f..0e6c98e 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SignRequestObject.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/SignRequestObject.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+package net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl;
 
 import java.security.interfaces.ECPrivateKey;
 import java.text.ParseException;
@@ -24,11 +24,10 @@ import java.util.function.Function;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import org.opensaml.messaging.context.MessageContext;
 import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
+import org.opensaml.messaging.handler.AbstractMessageHandler;
+import org.opensaml.messaging.handler.MessageHandlerException;
 import org.opensaml.security.credential.Credential;
 import org.opensaml.xmlsec.SignatureSigningParameters;
 import org.opensaml.xmlsec.context.SecurityParametersContext;
@@ -40,6 +39,7 @@ import com.nimbusds.jose.JOSEException;
 import com.nimbusds.jose.JOSEObjectType;
 import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.JWSObject.State;
 import com.nimbusds.jose.JWSSigner;
 import com.nimbusds.jose.crypto.ECDSASigner;
 import com.nimbusds.jose.crypto.MACSigner;
@@ -48,7 +48,7 @@ import com.nimbusds.jwt.JWT;
 import com.nimbusds.jwt.JWTClaimsSet;
 import com.nimbusds.jwt.SignedJWT;
 
-import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
 import net.shibboleth.oidc.security.context.JWTSecurityParametersContext;
 import net.shibboleth.oidc.security.credential.JWKCredential;
 import net.shibboleth.oidc.security.impl.CredentialConversionUtil;
@@ -58,17 +58,18 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
 import net.shibboleth.utilities.java.support.primitive.StringSupport;
 
 /**
- * Action that signs a request object and sets it to ???.
+ * Action that signs a request object and sets it as the request object to the authentication request.
  */
-public class SignRequestObject extends AbstractOIDCAuthenticationRequestAction {
+public class SignRequestObject extends AbstractMessageHandler {
     
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(SignRequestObject.class);
     
-    /**
-     * Strategy used to locate the {@link SecurityParametersContext} to use for signing.
-     */
-    @Nonnull private Function<ProfileRequestContext, JWTSecurityParametersContext> securityParametersLookupStrategy;
+    /** Strategy used to locate the {@link SecurityParametersContext} to use for signing. */
+    @Nonnull private Function<MessageContext, JWTSecurityParametersContext> securityParametersLookupStrategy;
+    
+    /** Strategy used to locate the {@link OIDCAuthenticationRequest} to sign.  */
+    @Nonnull private Function<MessageContext, OIDCAuthenticationRequest> authenticationRequestLookupStrategy;
     
     /** The signature signing parameters. */
     @Nullable private SignatureSigningParameters signatureSigningParameters;
@@ -82,10 +83,18 @@ public class SignRequestObject extends AbstractOIDCAuthenticationRequestAction {
     /** "typ" header to insert while signing. */
     @Nullable @NotEmpty private String typeHeader;
     
+    /** The stashed authentication request.*/
+    @Nullable private OIDCAuthenticationRequest authnRequest;
+    
     /** Constructor.*/
     public SignRequestObject() {
-        securityParametersLookupStrategy = new ChildContextLookup<>(JWTSecurityParametersContext.class)
-                .compose(new OutboundMessageContextLookup());
+        securityParametersLookupStrategy = new ChildContextLookup<>(JWTSecurityParametersContext.class);
+        authenticationRequestLookupStrategy = mc -> {
+            if (mc.getMessage() instanceof OIDCAuthenticationRequest) {
+                return (OIDCAuthenticationRequest)mc.getMessage();
+            }
+            return null;
+        };
     }
     
     /**
@@ -105,58 +114,79 @@ public class SignRequestObject extends AbstractOIDCAuthenticationRequestAction {
      * @param strategy lookup strategy
      */
     public void setSecurityParametersLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext, JWTSecurityParametersContext> strategy) {
+            @Nonnull final Function<MessageContext, JWTSecurityParametersContext> strategy) {
         ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
 
         securityParametersLookupStrategy =
                 Constraint.isNotNull(strategy, "SecurityParameterContext lookup strategy cannot be null");
     }
     
+    /**
+     * Set the strategy used to locate the {@link OIDCAuthenticationRequest} to use.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setAuthenticationRequestLookupStrategy(
+            @Nonnull final Function<MessageContext, OIDCAuthenticationRequest> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        authenticationRequestLookupStrategy =
+                Constraint.isNotNull(strategy, "AuthenticationRequestLookupStrategy lookup strategy cannot be null");
+    }
+    
+    
     /** {@inheritDoc} */
     @Override
-    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
-            @Nonnull final AuthenticationContext authenticationContext) {
-
-        if (!super.doPreExecute(profileRequestContext, authenticationContext)) {
+    protected boolean doPreInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+        
+        if (!super.doPreInvoke(messageContext)) {
+            return false;
+        }  
+        
+        authnRequest = authenticationRequestLookupStrategy.apply(messageContext);
+        if (authnRequest == null) {
+            log.debug("{} No authentication request available", getLogPrefix());
             return false;
         }
         
-        final JWTSecurityParametersContext secParamCtx = securityParametersLookupStrategy.apply(profileRequestContext);
+        final JWT requestObject = authnRequest.getRequestObject();
+        if (requestObject == null) {
+            log.debug("{} No JWT RequestObject found, nothing to sign", getLogPrefix());
+            return false;
+        }
+            
+        final JWTSecurityParametersContext secParamCtx = 
+                securityParametersLookupStrategy.apply(messageContext);
         if (secParamCtx == null) {
-            log.debug("{} no security parameters context are available", getLogPrefix());
+            log.debug("{} Message context did not contain signing parameters, "
+                    + "request object will not be signed", getLogPrefix());
             return false;
         }
-
+        
         signatureSigningParameters = secParamCtx.getSignatureSigningParameters();
         if (signatureSigningParameters == null || signatureSigningParameters.getSigningCredential() == null) {
-            log.debug("{} no signature signing credentials available", getLogPrefix());
-            return false;
-        }
-        final JWT requestObject = getAuthenticationRequest().getRequestObject();
-        if (requestObject == null) {
-            log.debug("{} no JWT request object found, nothing to sign", getLogPrefix());
+            log.debug("{} No signature signing credentials available", getLogPrefix());
             return false;
         }
         
         try {
             jwtClaimSetToSign = requestObject.getJWTClaimsSet();
         } catch (final ParseException e) {
-            log.debug("{} no JWT request object found, nothing to sign", getLogPrefix(), e);
+            log.debug("{} No JWT RequestObject found, nothing to sign", getLogPrefix(), e);
             return false;
         }
         
         credential = signatureSigningParameters.getSigningCredential();
-       
+        
         return true;
+         
     }
     
-    /** {@inheritDoc} */
     @Override
-    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
-            @Nonnull final AuthenticationContext authenticationContext) {
-
-        SignedJWT jwt = null;         
+    protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+                    
         try {
+            SignedJWT jwt = null; 
             final Algorithm jwsAlgorithm = resolveAlgorithm();
             final JWSSigner signer = getSigner(jwsAlgorithm);
             final JWSHeader.Builder headerBuilder = new JWSHeader.Builder(new JWSAlgorithm(jwsAlgorithm.getName()))
@@ -166,13 +196,27 @@ public class SignRequestObject extends AbstractOIDCAuthenticationRequestAction {
             }
             jwt = new SignedJWT(headerBuilder.build(), jwtClaimSetToSign);
             jwt.sign(signer);
+            if (log.isDebugEnabled() && !log.isTraceEnabled()) {
+                log.debug("{} Signed RequestObject", getLogPrefix());
+            } else if (log.isTraceEnabled()) {
+                log.debug("{} Signed RequestObject: {}", getLogPrefix(), jwt.serialize());
+            }            
+            
+            if (jwt.getState() != State.SIGNED) {
+                // Should not really happen, as JOSEException should be thrown
+                log.error("{} RequestObject was not signed", getLogPrefix());
+                throw new MessageHandlerException("RequestObject was not signed, unknown cause");
+            }
+            
+            // Add the signed JWT over the unsigned JWT
+            authnRequest.setRequestObject(jwt);
+            
         } catch (final JOSEException e) {
             log.error("{} Error signing claim set: {}", getLogPrefix(), e.getMessage());
-            ActionSupport.buildEvent(profileRequestContext, EventIds.UNABLE_TO_SIGN);
-            return;
+            throw new MessageHandlerException("Error signing claims set",e);
         }
-        // Add the signed JWT over the unsigned JWT
-        getAuthenticationRequest().setRequestObject(jwt);
+        
+        
     }
     
     /**
@@ -213,4 +257,6 @@ public class SignRequestObject extends AbstractOIDCAuthenticationRequestAction {
         return algorithm;
     }
 
+
+
 }
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index fd0fae4..9583830 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -67,6 +67,25 @@
         </constructor-arg>
     </bean>
     
+    <bean id="shibboleth.ChildLookup.Proxy.RelyingPartyContext"
+        parent="shibboleth.Functions.Compose">
+        <constructor-arg name="g">
+            <ref bean="shibboleth.ChildLookup.RelyingParty" />
+        </constructor-arg>
+        <constructor-arg name="f">
+            <ref bean="shibboleth.ChildLookup.Proxy.ProxyProfileRequestContext" />
+        </constructor-arg>
+    </bean>
+    
+    <bean id="shibboleth.ChildLookup.Proxy.ProxyProfileRequestContext" parent="shibboleth.Functions.Compose">        
+        <constructor-arg name="g">
+            <ref bean="shibboleth.ChildLookup.ProfileRequestContext" />
+        </constructor-arg>
+        <constructor-arg name="f">
+            <ref bean="shibboleth.ChildLookup.AuthenticationContext" />
+        </constructor-arg>
+    </bean>
+    
     <bean id="shibboleth.ChildLookup.OIDCProviderMetadataFromPeerEntityContext"
         parent="shibboleth.Functions.Compose">
         <constructor-arg name="g">
@@ -94,6 +113,16 @@
 
     <bean id="shibboleth.ChildLookup.Proxy.MessageContextLookup.Inbound" 
     class="net.shibboleth.idp.plugin.authn.oidc.rp.config.navigate.InboundMessageContextFromProxyPRC"/>
+    
+    <bean id="shibboleth.ChildLookup.Proxy.MessageContextLookup.Outbound" 
+    class="net.shibboleth.idp.plugin.authn.oidc.rp.config.navigate.OutboundMessageContextFromProxyPRC"/>
+    
+    <!-- Alias to use in the flow config -->
+    <alias name="shibboleth.ChildLookup.Proxy.MessageContextLookup.Inbound" alias="InboundMessageContextFromRootPRC"/>
+    <alias name="shibboleth.ChildLookup.Proxy.MessageContextLookup.Outbound" alias="OutboundMessageContextFromRootPRC"/>
+    <alias name="shibboleth.ChildLookup.Proxy.ProxyProfileRequestContext" alias="ProxyProfileRequestContextLookup"/>
+    
+    
 
     <!-- The authentication flow descriptor -->
 
@@ -135,16 +164,8 @@
     <bean id="shibboleth.authn.OIDC.externalAuthnPath" class="java.lang.String"
         c:_0="servletRelative:#{getObject('shibboleth.authn.OIDC.externalServletPath')}#{T(net.shibboleth.idp.plugin.authn.oidc.rp.impl.AuthorizationController).AUTHORIZE_PATH_SEGMENT}" />
 
-
-
     <bean id="shibboleth.oidc.rp.AuthorizationController"
-        p:redirectUriCreationStrategy="#{getObject('shibboleth.oidc.rp.RedirectUriCreationStrategy') ?: getObject('shibboleth.oidc.rp.DefaultRedirectUriCreationStrategy')}"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AuthorizationController" />
-
-    <bean id="shibboleth.oidc.rp.DefaultRedirectUriCreationStrategy"
-        c:callbackPath="#{getObject('shibboleth.authn.OIDC.externalServletPath')}/callback"
-        c:allowedOrigins="%{idp.authn.oidc.rp.client.redirecturl.allowedOrigins:}"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.DefaultRedirectUriCreationFunction" />
+       class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AuthorizationController" />
 
 
     <!-- OIDC OP information resolver service beans. -->
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 e79adc0..a768faa 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
@@ -99,74 +99,71 @@
 
     <bean id="SelectProfileConfiguration" class="net.shibboleth.idp.profile.impl.SelectProfileConfiguration"
         scope="prototype" p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
-        
-   
-    <bean id="InitializeAuthorizationRequest" 
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.InitializeAuthorizationRequest"
-        scope="prototype" p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"/>
+
+
+    <bean id="InitializeAuthorizationRequest"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.InitializeAuthorizationRequest" scope="prototype"
+        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
 
     <bean id="PopulateResponseTypeAndMode" scope="prototype"
         class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateResponseTypeAndMode"
-        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" 
-        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-        
-    <bean id="PopulateScopes" scope="prototype"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateScopes"
-        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" 
-        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-        
-    <bean id="PopulateNonce" scope="prototype"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateNonce"
-        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" 
-        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-    
+        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
+
+    <bean id="PopulateScopes" scope="prototype" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateScopes"
+        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
+
+    <bean id="PopulateNonce" scope="prototype" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateNonce"
+        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
+
     <bean id="PopulateEndpointURI" scope="prototype"
         class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateEndpointURI"
-        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" 
-        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-        
-    <bean id="PopulateACRs" scope="prototype"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateACRs"
-        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" 
-        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-         
+        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
+
+    <bean id="PopulateACRs" scope="prototype" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateACRs"
+        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
+
     <bean id="PopulateForceAuthenticationPrompt" scope="prototype"
         class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateForceAuthenticationPrompt"
-        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" 
-        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
+        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
 
     <!-- Build RequestObject if required -->
     <bean id="RequestObjectRequiredAndSupportedPredicate"
         class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.logic.RequestObjectRequiredAndSupported" />
 
-    
-    <bean id="PopulateRequestObjectSignatureSigningParameters" 
+
+    <bean id="PopulateRequestObjectSignatureSigningParameters"
         class="net.shibboleth.oidc.security.impl.PopulateJWTSignatureSigningParameters"
         c:strategy-ref="shibboleth.ChildLookup.Proxy.MessageContextLookup.Inbound"
         p:configurationLookupStrategy-ref="RequestObjectSignatureSigningConfigurationLookup"
         p:providerMetadataContextLookupStrategy-ref="shibboleth.ChildLookup.OIDCProviderMetadataContextFromOutbound"
-        p:signatureSigningParametersResolver-ref="shibboleth.authn.oidc.rp.RequestObjectSignatureSigningParametersResolver"/>
-        
-     <bean id="shibboleth.authn.oidc.rp.RequestObjectSignatureSigningParametersResolver"
+        p:signatureSigningParametersResolver-ref="shibboleth.authn.oidc.rp.RequestObjectSignatureSigningParametersResolver">
+        <property name="activationCondition">
+            <bean class="net.shibboleth.idp.plugin.authn.oidc.rp.config.logic.SignRequestObjectPredicate"
+                p:relyingPartyContextLookupStrategy-ref="shibboleth.ChildLookup.Proxy.RelyingPartyContext"/>
+        </property>
+    </bean>
+
+    <bean id="shibboleth.authn.oidc.rp.RequestObjectSignatureSigningParametersResolver"
         class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.RelyingPartyProxySigningParametersResolver"
-        p:providerMetadataAlgorithmLookupStrategy-ref="shibboleth.authn.oidc.rp.RequestObjectSupportedSignatureSigningAlgorithms"/>
-     
-     <bean id="shibboleth.authn.oidc.rp.RequestObjectSupportedSignatureSigningAlgorithms"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.config.RequestObjectSupportedSignatureSigningAlgorithms"/>
-    
+        p:providerMetadataAlgorithmLookupStrategy-ref="shibboleth.authn.oidc.rp.RequestObjectSupportedSignatureSigningAlgorithms" />
+
+    <bean id="shibboleth.authn.oidc.rp.RequestObjectSupportedSignatureSigningAlgorithms"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.config.RequestObjectSupportedSignatureSigningAlgorithms" />
+
     <bean id="RequestObjectSignatureSigningConfigurationLookup" lazy-init="true"
         class="net.shibboleth.idp.plugin.authn.oidc.rp.config.navigate.RequestObjectSignatureSigningConfigurationLookupFunction"
         p:relyingPartyConfigurationResolver-ref="shibboleth.RelyingPartyConfigurationResolver" />
 
-    <bean id="BuildRequestObject" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.BuildRequestObject" 
-        scope="prototype"
-        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" 
-        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/> 
-        
-    <bean id="SignRequestObject" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.SignRequestObject" scope="prototype"
-        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" 
-        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-   
+    <bean id="BuildRequestObject" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.BuildRequestObject"
+        scope="prototype" p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"
+        p:claimsSetIsValidPredicate="#{getObject('shibboleth.authn.oidc.rp.RequestObjectClaimsSetIsValidPredicate')}" />
 
 
     <!-- Message Encoder factory is a prototype to allow reuse of the encoders -->
@@ -182,26 +179,45 @@
 
     <bean id="HTTPRedirectAuthnRequestEncoder"
         class="net.shibboleth.oidc.profile.encoder.impl.HTTPRedirectAuthnRequestEncoder" init-method=""
-        scope="prototype" p:httpServletResponse-ref="shibboleth.HttpServletResponse" />
+        scope="prototype" p:httpServletResponse-ref="shibboleth.HttpServletResponse"
+        p:authorizationParamsAreValidPredicate="#{getObject('shibboleth.authn.oidc.rp.AuthzParamsValidPredicate')}" />
 
     <bean id="HTTPPostAuthnRequestEncoder"
         class="net.shibboleth.oidc.profile.encoder.impl.HTTPPostAuthnRequestEncoder" init-method="" scope="prototype"
-        p:velocityEngine-ref="shibboleth.VelocityEngine" p:httpServletResponse-ref="shibboleth.HttpServletResponse" />
+        p:velocityEngine-ref="shibboleth.VelocityEngine" p:httpServletResponse-ref="shibboleth.HttpServletResponse"
+        p:authorizationParamsAreValidPredicate="#{getObject('shibboleth.authn.oidc.rp.AuthzParamsValidPredicate')}" />
 
 
     <bean id="EncodeMessage" class="org.opensaml.profile.action.impl.EncodeMessage" scope="prototype"
         p:messageEncoderFactory-ref="messageEncoderFactory" p:httpServletResponse-ref="shibboleth.HttpServletResponse" />
 
-    <!-- TODO: Place holder for message handlers -->
     <bean id="PreEncodeMessageHandler" class="org.opensaml.messaging.handler.impl.BasicMessageHandlerChain"
         scope="prototype">
         <property name="handlers">
             <list>
+                <bean id="AddState" class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.AddState"
+                    scope="prototype"
+                    p:stateGenerationStrategy="#{getObject('shibboleth.authn.oidc.rp.StateGenerationStrategy')}" />
+
+                <bean id="AddRedirectURI"
+                    class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.AddRedirectURI" scope="prototype"
+                    p:redirectUriCreationStrategy="#{getObject('shibboleth.oidc.rp.RedirectUriCreationStrategy') ?: getObject('shibboleth.oidc.rp.DefaultRedirectUriCreationStrategy')}" />
 
+                <bean id="BuildPlainRequestObjectJWT"
+                    class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.BuildPlainRequestObjectJWT"
+                    scope="prototype" />
+
+                <bean id="SignRequestObject"
+                    class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.SignRequestObject" scope="prototype" />
             </list>
         </property>
     </bean>
 
+    <bean id="shibboleth.oidc.rp.DefaultRedirectUriCreationStrategy"
+        c:callbackPath="#{getObject('shibboleth.authn.OIDC.externalServletPath')}/callback"
+        c:allowedOrigins="%{idp.authn.oidc.rp.client.redirecturl.allowedOrigins:}"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.DefaultRedirectUriCreationFunction" />
+
     <!-- Message Decoding -->
     <bean id="messageDecoderFactory" class="net.shibboleth.idp.saml.profile.impl.SpringAwareMessageDecoderFactory">
         <property name="beanMappings">
@@ -251,7 +267,10 @@
             </bean>
         </property>
     </bean>
-
+    
+    <bean id="IsCodeFlow" class="net.shibboleth.idp.plugin.authn.oidc.rp.config.logic.IsCodeFlowPredicate"/>
+    <bean id="IsHybridFlow" class="net.shibboleth.idp.plugin.authn.oidc.rp.config.logic.IsHybridFlowPredicate"/>
+    <bean id="IsImplicitFlow" class="net.shibboleth.idp.plugin.authn.oidc.rp.config.logic.IsImplicitFlowPredicate"/>
 
     <!-- CODE flow beans -->
 
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 b2c98a9..59ccafb 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
@@ -23,7 +23,6 @@
         <!-- Nest new PRC inside AC -->
         <evaluate expression="InitializeProxyProfileRequestContext" />
         <evaluate expression="FlowStartPopulateAuditContext" />
-        <!-- Init inbound msg context and create OP Peer -->
         <evaluate expression="PrepareOIDCInboundMessageContext" />
 
         <evaluate expression="OIDCProviderMetadataLookup" />
@@ -39,32 +38,32 @@
         <evaluate expression="PopulateScopes" />
         <evaluate expression="PopulateNonce" />
         <evaluate expression="PopulateForceAuthenticationPrompt" />
-        <evaluate expression="PopulateEndpointURI"/>
-        <evaluate expression="PopulateACRs"/>
-        <!-- <evaluate expression="PostRequestPopulateAuditContext" /> 
-        <evaluate expression="WriteAuditLog" /> -->
+        <evaluate expression="PopulateEndpointURI" />
+        <evaluate expression="PopulateACRs" />
+        <!-- <evaluate expression="PostRequestPopulateAuditContext" /> <evaluate expression="WriteAuditLog" /> -->
 
         <!-- <evaluate expression="InitializeMessageChannelSecurityContext" /> -->
         <evaluate expression="'proceed'" />
 
         <transition on="proceed" to="RequestObjectRequiredAndSupported" />
     </action-state>
-    
-    <!-- Is a request object requested by the config, and does the OP support it?  -->
+
+    <!-- Is a request object requested by the config, and does the OP support it? -->
     <decision-state id="RequestObjectRequiredAndSupported">
-        <if test="RequestObjectRequiredAndSupportedPredicate.test(opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext').getSubcontext('org.opensaml.profile.context.ProfileRequestContext'))"
-            then="BuildRequestObject" else="AuthRequest"/>   
+        <if
+            test="RequestObjectRequiredAndSupportedPredicate.test(opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext').getSubcontext('org.opensaml.profile.context.ProfileRequestContext'))"
+            then="BuildRequestObject" else="AuthnRequest" />
     </decision-state>
-    
+
     <action-state id="BuildRequestObject">
-        <evaluate expression="PopulateRequestObjectSignatureSigningParameters"/>
+        <evaluate expression="PopulateRequestObjectSignatureSigningParameters" />
         <evaluate expression="BuildRequestObject" />
-        <evaluate expression="SignRequestObject" />
-        <!-- <evaluate expression="'proceed'" />
-        <transition on="proceed" to="AuthRequest" /> -->
+        <!-- We can not sign and encrypt the RO here, it has to be done as part of the controller so we can add state etc. -->
+        <evaluate expression="'proceed'" />
+        <transition on="proceed" to="AuthRequest" />
     </action-state>
-    
-    <view-state id="AuthRequest"
+
+    <view-state id="AuthnRequest"
         view="externalRedirect:#{T(net.shibboleth.idp.authn.ExternalAuthentication).getExternalRedirect(flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.OIDC.externalAuthnPath'), flowExecutionContext.getKey().toString())}">
         <attribute name="csrf_excluded" value="true" type="boolean" />
         <on-render>
@@ -93,27 +92,10 @@
 
     <!-- Switch flow path based on OIDC grant_type used. TODO possible places for an NPE, use strategy? -->
     <decision-state id="SwitchOnGrantType">
-        <!-- check a null response_type first, should never get here -->
-        <if
-            test="opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext')
-                            .getSubcontext('org.opensaml.profile.context.ProfileRequestContext').getOutboundMessageContext().getMessage().getResponseType() == null"
-            then="UnsupportedFlow" />
-        <!-- Check for CODE flow -->
-        <if
-            test="opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext')
-                            .getSubcontext('org.opensaml.profile.context.ProfileRequestContext').getOutboundMessageContext().getMessage().getResponseType().impliesCodeFlow()"
-            then="CodeFlow" />
-        <!-- Check for Hybrid flow -->
-        <if
-            test="opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext')
-                            .getSubcontext('org.opensaml.profile.context.ProfileRequestContext').getOutboundMessageContext().getMessage().getResponseType().impliesImplicitFlow()"
-            then="HybridFlow" />
-        <!-- Check for IMPLICIT flow -->
+        <if test="IsCodeFlow.test(OutboundMessageContextFromRootPRC.apply(opensamlProfileRequestContext))" then="CodeFlow" />
+        <if test="IsHybridFlow.test(OutboundMessageContextFromRootPRC.apply(opensamlProfileRequestContext))" then="HybridFlow" />
         <!-- final IF has an else if an unsupported flow is used (should not happen) -->
-        <if
-            test="opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext')
-                            .getSubcontext('org.opensaml.profile.context.ProfileRequestContext').getOutboundMessageContext().getMessage().getResponseType().impliesHybridFlow()"
-            then="ImplicitFlow" else="UnsupportedFlow" />
+        <if test="IsImplicitFlow.test(OutboundMessageContextFromRootPRC.apply(opensamlProfileRequestContext))" then="ImplicitFlow" else="UnsupportedFlow" />
     </decision-state>
 
     <action-state id="CodeFlow">
@@ -129,17 +111,17 @@
     <!-- TODO claim validation will differ per grant_type -->
     <action-state id="ValidateToken">
         <evaluate expression="PopulateIDTokenDecryptionParameters" />
-        <evaluate expression="DecryptJWT"/>
+        <evaluate expression="DecryptJWT" />
         <evaluate expression="PopulateIDTokenSignatureValidationParameters" />
-        <!-- TODO Not sure if HandleIDTokenValidation needs to be a message chain?  -->
+        <!-- TODO Not sure if HandleIDTokenValidation needs to be a message chain? -->
         <evaluate expression="HandleIDTokenValidation" />
         <evaluate expression="ValidateIDTokenClaims" />
         <evaluate expression="'proceed'" />
         <transition on="proceed" to="CheckUserInfoClaimsRequired" />
     </action-state>
 
-    <decision-state id="CheckUserInfoClaimsRequired">        
-            <if test="CheckUserInfoRequiredCondition.test(opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext').getSubcontext('org.opensaml.profile.context.ProfileRequestContext'))"
+    <decision-state id="CheckUserInfoClaimsRequired">
+        <if test="CheckUserInfoRequiredCondition.test(ProxyProfileRequestContextLookup.apply(opensamlProfileRequestContext))"
             then="UserInfoRequest" else="FinalizeResponse" />
         <!-- if else here, we need to set id_token claims into EndUserClaimsContext -->
     </decision-state>
@@ -152,22 +134,22 @@
     </action-state>
 
     <!-- A plain JWT will skip token validation and go straight to claims validation -->
-    <decision-state id="CheckUserInfoResponseType">        
-            <if test="CheckUserInfoPlainResponseTypeCondition.test(opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext').getSubcontext('org.opensaml.profile.context.ProfileRequestContext'))"
-            then="ValidateUserInfoPlaimClaimsSet" else="ValidateUserInfoJWT"/>
+    <decision-state id="CheckUserInfoResponseType">
+        <if test="CheckUserInfoPlainResponseTypeCondition.test(ProxyProfileRequestContextLookup.apply(opensamlProfileRequestContext))"
+            then="ValidateUserInfoPlaimClaimsSet" else="ValidateUserInfoJWT" />
     </decision-state>
-    
+
     <!-- Actions to perform if the UserInfo response is a JWT type -->
     <action-state id="ValidateUserInfoJWT">
         <evaluate expression="PopulateUserInfoDecryptionParameters" />
-        <evaluate expression="DecryptUserInfoJWT"/>
+        <evaluate expression="DecryptUserInfoJWT" />
         <evaluate expression="PopulateUserInfoTokenSignatureValidationParameters" />
-        <evaluate expression="HandleUserInfoTokenValidation" />    
-        <evaluate expression="ValidateUserInfoTokenClaims" />      
+        <evaluate expression="HandleUserInfoTokenValidation" />
+        <evaluate expression="ValidateUserInfoTokenClaims" />
         <evaluate expression="'proceed'" />
-        <transition on="proceed" to="FinalizeResponse" />    
+        <transition on="proceed" to="FinalizeResponse" />
     </action-state>
-    
+
     <!-- Plain UserInfo response types will skip straight to this stage -->
     <action-state id="ValidateUserInfoPlaimClaimsSet">
         <evaluate expression="ValidateUserInfoPlainResponseClaims" />
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 1550a2a..98b636e 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
@@ -20,6 +20,7 @@ package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
 import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
@@ -27,9 +28,11 @@ import static org.testng.Assert.assertTrue;
 import java.io.IOException;
 import java.net.URI;
 import java.net.URLEncoder;
+import java.util.ArrayList;
 import java.util.Set;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -40,10 +43,13 @@ import org.opensaml.messaging.decoder.MessageDecodingException;
 import org.opensaml.messaging.decoder.servlet.AbstractHttpServletRequestMessageDecoder;
 import org.opensaml.messaging.encoder.MessageEncoder;
 import org.opensaml.messaging.encoder.MessageEncodingException;
+import org.opensaml.messaging.handler.MessageHandler;
+import org.opensaml.messaging.handler.impl.BasicMessageHandlerChain;
 import org.opensaml.profile.action.AbstractProfileAction;
 import org.opensaml.profile.action.ActionSupport;
 import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.xmlsec.SignatureSigningParameters;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.TestPropertySource;
@@ -64,7 +70,11 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.nimbusds.oauth2.sdk.ResponseType;
+import com.nimbusds.oauth2.sdk.Scope;
+import com.nimbusds.oauth2.sdk.id.Audience;
 import com.nimbusds.oauth2.sdk.id.ClientID;
+import com.nimbusds.oauth2.sdk.id.Issuer;
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
 
 import net.shibboleth.idp.authn.AuthenticationFlowDescriptor;
 import net.shibboleth.idp.authn.ExternalAuthentication;
@@ -75,6 +85,10 @@ import net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCAuthnContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.AddRedirectURI;
+import net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.AddState;
+import net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.BuildPlainRequestObjectJWT;
+import net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.SignRequestObject;
 import net.shibboleth.idp.plugin.authn.test.flow.mock.IdPPropertyConfigurer;
 import net.shibboleth.idp.session.IdPSession;
 import net.shibboleth.idp.session.context.SessionContext;
@@ -83,11 +97,12 @@ import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration.OIDCHtt
 import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
 import net.shibboleth.oidc.profile.decoding.OIDCMessageDecoder;
 import net.shibboleth.oidc.profile.encoder.impl.AbstractOIDCMessageEncoder;
+import net.shibboleth.oidc.security.context.JWTSecurityParametersContext;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.net.HttpServletSupport;
 import net.shibboleth.utilities.java.support.net.URLBuilder;
 
-
+/** Integration tests, using other 'live' actions, for the AuthorizationController.*/
 @ContextConfiguration(classes = {AuthorizationController.class, IdPPropertyConfigurer.class})
 @WebAppConfiguration
 @TestPropertySource(properties = {"shibboleth.authn.OIDC.externalAuthnPath=/Authn/OIDC/RP",})
@@ -99,6 +114,9 @@ public class AuthorizationControllerTest extends AbstractTestNGSpringContextTest
     /** The endpoint to redirect the user-agent to.*/
     @Nonnull private final String ENDPOINT_URI = "https://op.example.com/";
     
+    /** A client_secret to use.*/
+    @Nonnull private final String CLIENT_SECRET = "Xp2s5v8y/B?E(H+MbQeThWmYq3t6w9z$";
+    
     /** The issuer or OP identifier.*/
     @Nonnull private final String ISSUER = "https://op.example.com";
     
@@ -117,6 +135,9 @@ public class AuthorizationControllerTest extends AbstractTestNGSpringContextTest
     /** The mock http servlet request.*/
     @Nonnull @Autowired private HttpServletRequest request;
     
+    /** The PRC that has been setup.*/
+    @Nullable private ProfileRequestContext context;
+    
     /**
      * Setup. 
      * 
@@ -131,13 +152,11 @@ public class AuthorizationControllerTest extends AbstractTestNGSpringContextTest
         assertNotNull(controller);
         assertNotNull(response);
         assertNotNull(request);
-        controller.setRedirectUriCreationStrategy(
-                new DefaultRedirectUriCreationFunction("/Authn/OIDC/RP/callback", Set.of("http://localhost")));
 
         mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
        
         //add and mock attributes of the servlet context.
-        exportServletContextAttributes();
+        context = exportServletContextAttributes();
     }
     
     @Test
@@ -157,24 +176,72 @@ public class AuthorizationControllerTest extends AbstractTestNGSpringContextTest
         assertTrue(result.getResponse().getRedirectedUrl().contains("client_id"));
         assertTrue(result.getResponse().getRedirectedUrl().contains("response_type"));
         assertTrue(result.getResponse().getRedirectedUrl().contains("scope"));
+        assertTrue(result.getResponse().getRedirectedUrl().contains("state"));
+        // Should not contain the request object
+        assertFalse(result.getResponse().getRedirectedUrl().contains("request"));
     }
     
-    //TODO the callback method
+    @Test
+    public void testSuccessfulAuthorizeRequest_WithRequestObject() throws Exception {
+       
+        assertTrue(context.getSubcontext(AuthenticationContext.class)
+                .getSubcontext(ProfileRequestContext.class)
+                .getOutboundMessageContext().getMessage() instanceof OIDCAuthenticationRequest);
+        // Add a request object into the request
+        final OIDCAuthenticationRequest request = 
+                (OIDCAuthenticationRequest) context.getSubcontext(AuthenticationContext.class)
+                .getSubcontext(ProfileRequestContext.class)
+                .getOutboundMessageContext().getMessage();
+        addRequestObject(request);
+        
+        final MvcResult result = mockMvc.perform(get("/Authn/OIDC/RP/authz")
+                .param("conversation", "e1s1").characterEncoding("UTF-8"))
+                .andDo(print())
+                .andExpect(status().is3xxRedirection()).andReturn();
+        assertNotNull(result.getResponse().getHeader("Location"));
+        final ExternalAuthenticationContext extContext = extractExternalAuthContext();
+        // assert no error in the context
+        assertNull(extContext.getAuthnError());       
+        //basic check of the redirection URL.
+        assertTrue(result.getResponse().getRedirectedUrl().contains(ENDPOINT_URI));
+        assertTrue(result.getResponse().getRedirectedUrl().contains(URLEncoder.encode(REDIRECT_URI, "UTF-8")));
+        assertTrue(result.getResponse().getRedirectedUrl().contains("client_id"));
+        assertTrue(result.getResponse().getRedirectedUrl().contains("response_type"));
+        assertTrue(result.getResponse().getRedirectedUrl().contains("scope"));
+        assertTrue(result.getResponse().getRedirectedUrl().contains("request"));
+        assertTrue(result.getResponse().getRedirectedUrl().contains("state"));
+    }
     
+    /** Add a simple RequestObject JWT to the authentication request.*/
+    private void addRequestObject(@Nonnull final OIDCAuthenticationRequest request) {
+        
+        final ClaimsSet requestObjectClaims = new ClaimsSet();
+        requestObjectClaims.setClaim("client_id", "mock_client");
+        requestObjectClaims.setAudience(new Audience("mock-op"));
+        requestObjectClaims.setIssuer(new Issuer("mock_client"));
+        requestObjectClaims.setClaim("scope", new Scope("openid"));
+
+        request.setRequestObjectClaimsSet(requestObjectClaims);
+        
+    }
+   
     /**
      * Export the FlowExecutor to the servlet context with the correct set of configured contexts. Mimicking the
      * IdP's configuration of the {@link ServletContextAttributeExporter}.
      * 
+     * @return the profile request context.
+     * 
      * @throws Exception on error.
      */
-    private void exportServletContextAttributes() throws Exception {
+    private ProfileRequestContext exportServletContextAttributes() throws Exception {
 
         final FlowExecutorImpl mockFlowExecutor = Mockito.mock(FlowExecutorImpl.class);
         final FlowExecutionRepository mockFlowExecutionRepo = Mockito.mock(FlowExecutionRepository.class);
         final FlowExecution mockFlowExecution = Mockito.mock(FlowExecution.class);
 
         final MutableAttributeMap<Object> map = new LocalAttributeMap<Object>();
-        map.put(ProfileRequestContext.BINDING_KEY, buildProfileRequestContext());
+        final var prc = buildProfileRequestContext();
+        map.put(ProfileRequestContext.BINDING_KEY, prc);
 
         Mockito.when(mockFlowExecutor.getExecutionRepository()).thenReturn(mockFlowExecutionRepo);
         final CompositeFlowExecutionKey key = new CompositeFlowExecutionKey("1", "1");
@@ -184,6 +251,64 @@ public class AuthorizationControllerTest extends AbstractTestNGSpringContextTest
 
         // overwrites previous if set from previous method executions.
         servletContext.setAttribute(ExternalAuthentication.SWF_KEY, mockFlowExecutor);
+        
+        // add a security params context to nested prc
+        addSecurityParametersContext(
+                prc.getSubcontext(AuthenticationContext.class).getSubcontext(ProfileRequestContext.class));
+        
+        // add standard set of preencode message handlers to authn context under ac
+        addPreEncodeMessageHandler(
+                prc.getSubcontext(AuthenticationContext.class).getSubcontext(OIDCAuthnContext.class));
+        
+        return prc;
+    }
+    
+    /**
+     * Add the standard set of pre encode message handlers e.g. those to sign a request object if present.
+     * 
+     * @param authnContext the authentication context
+     * @throws ComponentInitializationException on error
+     */
+    private void addPreEncodeMessageHandler(@Nonnull final OIDCAuthnContext authnContext) 
+            throws ComponentInitializationException {
+        
+        final var chainingMsgHandler = new BasicMessageHandlerChain();
+        final var handlers = new ArrayList<MessageHandler>();
+        
+        final var addState = new AddState();
+        addState.initialize();
+        final var addRedirectUri = new AddRedirectURI();
+        addRedirectUri.setRedirectUriCreationStrategy(
+                new DefaultRedirectUriCreationFunction("/Authn/OIDC/RP/callback", Set.of("http://localhost")));
+        addRedirectUri.initialize();
+        final var signer = new SignRequestObject();
+        signer.initialize();
+        final var buildRequestObjectJwt = new BuildPlainRequestObjectJWT();
+        buildRequestObjectJwt.initialize();
+        
+        
+        handlers.add(addState);
+        handlers.add(addRedirectUri);
+        handlers.add(buildRequestObjectJwt);
+        handlers.add(signer);
+        chainingMsgHandler.setHandlers(handlers);        
+        authnContext.setOutboundMessageHandler(chainingMsgHandler);
+        
+    }
+    
+    /**
+     * A a security parameters context, almost always for request object signing/encryption by this point.
+     * 
+     * @param prc the profile request context
+     */
+    private void addSecurityParametersContext(@Nonnull final ProfileRequestContext prc) {
+        // Create a sec context under the nested prc outbound msg context
+        final var secContext = prc.getOutboundMessageContext().getSubcontext(JWTSecurityParametersContext.class, true);
+        
+        final var sigParams = new SignatureSigningParameters();
+        sigParams.setSignatureAlgorithm("HS256");
+        sigParams.setSigningCredential(TestCredentialHelper.createClientSecretCredential(CLIENT_SECRET));
+        secContext.setSignatureSigningParameters(sigParams);
     }
     
     /**
@@ -247,9 +372,7 @@ public class AuthorizationControllerTest extends AbstractTestNGSpringContextTest
         
         final OIDCPeerEntityContext peerEntity = new OIDCPeerEntityContext();
         peerEntity.setIdentifier(ISSUER);
-        final OAuth2ClientContext clientContext = peerEntity.getSubcontext(OAuth2ClientContext.class, true);
-        
-        
+        final OAuth2ClientContext clientContext = peerEntity.getSubcontext(OAuth2ClientContext.class, true);                
         prc.getOutboundMessageContext().addSubcontext(peerEntity);
     
         return rootPrc;
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
index 607412e..2d3f7bc 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
@@ -475,67 +475,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
                 new URI("/idp/profile/Authn/OIDC/RP/callback"
                         + "?state=8df98fd63a53fa5b5433d6f8754bca5d.65317332&code=z8C2DCp6sn0D9aGbEqlrFesdPVRXPtDX"));
     }
-    
-    
-    /**
-     * Create a direct encryption {@link JWKCredential} from the given shared secret.
-     * 
-     * @param secret the secret to convert to a {@link JWKCredential}.
-     * 
-     * @return the credential
-     */
-    private JWKCredential createDirectEncryptionCredentialFromSharedSecret(final String secret) {
-        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
-        jwkCredential.setSecretKey(new SecretKeySpec(JWSAssemblyUtils.getSecretBytes(secret), "NONE"));
-        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
-        jwkCredential.setUsageType(UsageType.UNSPECIFIED);
-        jwkCredential.getCredentialContextSet().add(
-                new JWKEncryptionCredentialContext(EncryptionMethod.A256GCM));
-        jwkCredential.setKid("mockKey");
-        jwkCredential.getKeyNames().add("mockKey");
-        jwkCredential.setAlgorithm(JWEAlgorithm.DIR);
-        return jwkCredential;
-    }
-    
-    /**
-     * Create a simple client credential from from the given shared secret.
-     * 
-     * @param secret the secret to convert to a {@link JWKCredential}.
-     * 
-     * @return the credential
-     */
-    //TODO used for both signing and encryption, so alg needs to reflect this
-    private JWKCredential createClientSecretCredential(final String secret) {
-        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
-        jwkCredential.setSecretKey(new SecretKeySpec(JWSAssemblyUtils.getSecretBytes(secret), "NONE"));
-        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
-        jwkCredential.setUsageType(UsageType.UNSPECIFIED);
-        jwkCredential.setKid("mockKey");
-        jwkCredential.getKeyNames().add("mockKey");
-        return jwkCredential;
-    }
-    
-    /**
-     * Create a direct encryption {@link JWKCredential} from the given shared secret.
-     * 
-     * @param secret the secret to convert to a {@link JWKCredential}.
-     * 
-     * @return the credential
-     * @throws JOSEException 
-     */
-    private JWKCredential createKeyEncryptionCredential(final RSAKey secret) throws JOSEException {
-        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
-        jwkCredential.setPrivateKey(secret.toPrivateKey());
-        jwkCredential.setPublicKey(secret.toPublicKey());
-        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
-        jwkCredential.setUsageType(UsageType.ENCRYPTION);
-        
-        jwkCredential.setKid(secret.getKeyID());
-        jwkCredential.getKeyNames().add("mockKey");
-        jwkCredential.setAlgorithm(secret.getAlgorithm());
-        return jwkCredential;
-    }
-    
+      
     
     @Override
     @Nonnull protected ProfileRequestContext buildProfileRequestContext(@Nonnull final String flowId,
@@ -575,9 +515,14 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
                             buildProfileRequestContext("authn/OIDCRelyingParty",false,true));
         updateFlowExecution(flowExecution);
         flowExecution.start(inputMap, externalContext);    
-        assertCurrentStateEquals("AuthRequest");
+        assertCurrentStateEquals("AuthnRequest");
     }
     
+    /**
+     * Test to the authorization redirect using an OP who's RP config in XML is set to use a request object.
+     * 
+     * @throws Exception on error.
+     */
     @Test
     public void testFlowToAuthorizationRedirect_UsingRequestObject() throws Exception {
         setFlowPath(FLOW);
@@ -609,7 +554,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
         updateFlowExecution(flowExecution);
         flowExecution.start(inputMap, externalContext);    
-        assertCurrentStateEquals("AuthRequest");
+        assertCurrentStateEquals("AuthnRequest");
     }
     
     @Test
@@ -650,7 +595,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         flowExecution.getConversationScope().put("opensamlProfileRequestContext", prc);
         updateFlowExecution(flowExecution);
         flowExecution.start(inputMap, externalContext);    
-        assertCurrentStateEquals("AuthRequest");
+        assertCurrentStateEquals("AuthnRequest");
     }
     
     /**
@@ -745,7 +690,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         
         //set start view and ending event to transition on.
         externalContext.setEventId("proceed");
-        setCurrentState("AuthRequest");       
+        setCurrentState("AuthnRequest");       
         resumeFlow(externalContext);
         
         mockOPServer.shutdown();
@@ -797,7 +742,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         
         //set start view and ending event to transition on.
         externalContext.setEventId("proceed");
-        setCurrentState("AuthRequest");       
+        setCurrentState("AuthnRequest");       
         resumeFlow(externalContext);
         
         mockOPServer.shutdown();
@@ -849,7 +794,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         
         //set start view and ending event to transition on.
         externalContext.setEventId("proceed");
-        setCurrentState("AuthRequest");       
+        setCurrentState("AuthnRequest");       
         resumeFlow(externalContext);
         
         mockOPServer.shutdown();
@@ -901,7 +846,8 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
                     .getSubcontext(RelyingPartyContext.class)
                     .getProfileConfig();
         
-        partyConfig.setClientCredential(createDirectEncryptionCredentialFromSharedSecret(CLIENT_SECRET));
+        partyConfig.setClientCredential(
+                TestCredentialHelper.createDirectEncryptionCredentialFromSharedSecret(CLIENT_SECRET));
         // Set a default security config for the profile config
         final OIDCSecurityConfiguration secConfig = new OIDCSecurityConfiguration();
         
@@ -974,7 +920,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         
         //set start view and ending event to transition on.
         externalContext.setEventId("proceed");
-        setCurrentState("AuthRequest");       
+        setCurrentState("AuthnRequest");       
         resumeFlow(externalContext);
         
         mockOPServer.shutdown();
@@ -1069,7 +1015,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         
         //set start view and ending event to transition on.
         externalContext.setEventId("proceed");
-        setCurrentState("AuthRequest");       
+        setCurrentState("AuthnRequest");       
         resumeFlow(externalContext);
         
         mockOPServer.shutdown();
@@ -1122,7 +1068,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         
         //set start view and ending event to transition on.
         externalContext.setEventId("proceed");
-        setCurrentState("AuthRequest");       
+        setCurrentState("AuthnRequest");       
         resumeFlow(externalContext);
         
         mockOPServer.shutdown();
@@ -1149,7 +1095,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         final RelyingPartyContext partyContext = new RelyingPartyContext();
         final OIDCAuthorizationConfiguration partyConfig = new OIDCAuthorizationConfiguration();  
         partyContext.setProfileConfig(partyConfig);
-        partyConfig.setClientCredential(createClientSecretCredential(CLIENT_SECRET));
+        partyConfig.setClientCredential(TestCredentialHelper.createClientSecretCredential(CLIENT_SECRET));
         partyConfig.setTokenEndpointAuthMethods(Set.of("client_secret_basic"));
         partyConfig.setClientId(CLIENT_ID);
         final RelyingPartyConfiguration rPartyConfig = new RelyingPartyConfiguration();
@@ -1232,7 +1178,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         
         //set start view and ending event to transition on.
         externalContext.setEventId("proceed");
-        setCurrentState("AuthRequest");       
+        setCurrentState("AuthnRequest");       
         resumeFlow(externalContext);
         
         //assert success conditions
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestCredentialHelper.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestCredentialHelper.java
new file mode 100644
index 0000000..0569db9
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestCredentialHelper.java
@@ -0,0 +1,86 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.time.Duration;
+
+import javax.crypto.spec.SecretKeySpec;
+
+import org.opensaml.security.credential.UsageType;
+
+import com.nimbusds.jose.EncryptionMethod;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.jwk.RSAKey;
+
+import net.shibboleth.oidc.security.credential.BasicExpiringJWKCredential;
+import net.shibboleth.oidc.security.credential.JWKCredential;
+import net.shibboleth.oidc.security.credential.impl.JWKEncryptionCredentialContext;
+import net.shibboleth.oidc.security.impl.JWSAssemblyUtils;
+
+/** Helper that creates different credentials.*/
+public final class TestCredentialHelper {
+    
+    private TestCredentialHelper() {
+        
+    }
+    
+    /**
+     * Create a simple client credential from from the given shared secret.
+     * 
+     * @param secret the secret to convert to a {@link JWKCredential}.
+     * 
+     * @return the credential
+     */
+    //TODO used for both signing and encryption, so alg needs to reflect this
+    public static JWKCredential createClientSecretCredential(final String secret) {
+        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+        jwkCredential.setSecretKey(new SecretKeySpec(JWSAssemblyUtils.getSecretBytes(secret), "NONE"));
+        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+        jwkCredential.setUsageType(UsageType.UNSPECIFIED);
+        jwkCredential.setKid("mockKey");
+        jwkCredential.getKeyNames().add("mockKey");
+        return jwkCredential;
+    }
+    
+    /**
+     * Create a direct encryption {@link JWKCredential} from the given shared secret.
+     * 
+     * @param secret the secret to convert to a {@link JWKCredential}.
+     * 
+     * @return the credential
+     */
+    public static JWKCredential createDirectEncryptionCredentialFromSharedSecret(final String secret) {
+        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+        jwkCredential.setSecretKey(new SecretKeySpec(JWSAssemblyUtils.getSecretBytes(secret), "NONE"));
+        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+        jwkCredential.setUsageType(UsageType.UNSPECIFIED);
+        jwkCredential.getCredentialContextSet().add(
+                new JWKEncryptionCredentialContext(EncryptionMethod.A256GCM));
+        jwkCredential.setKid("mockKey");
+        jwkCredential.getKeyNames().add("mockKey");
+        jwkCredential.setAlgorithm(JWEAlgorithm.DIR);
+        return jwkCredential;
+    }
+    
+    
+    /**
+     * Create a direct encryption {@link JWKCredential} from the given shared secret.
+     * 
+     * @param secret the secret to convert to a {@link JWKCredential}.
+     * 
+     * @return the credential
+     * @throws JOSEException 
+     */
+    public static JWKCredential createKeyEncryptionCredential(final RSAKey secret) throws JOSEException {
+        final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+        jwkCredential.setPrivateKey(secret.toPrivateKey());
+        jwkCredential.setPublicKey(secret.toPublicKey());
+        jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+        jwkCredential.setUsageType(UsageType.ENCRYPTION);
+        
+        jwkCredential.setKid(secret.getKeyID());
+        jwkCredential.getKeyNames().add("mockKey");
+        jwkCredential.setAlgorithm(secret.getAlgorithm());
+        return jwkCredential;
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml b/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
index 15518de..4cb7807 100644
--- a/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
+++ b/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
@@ -53,7 +53,7 @@
         <bean id="TestRequestObject" parent="RelyingPartyByName" c:relyingPartyIds="https://localhost:9919">
             <property name="profileConfigurations">
                 <list>
-                    <bean parent="OIDC.SSO" p:useRequestObject="true"/>
+                    <bean parent="OIDC.SSO" p:useRequestObject="true" p:signRequestObject="true"/>
                 </list>
             </property>
         </bean>

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


More information about the commits mailing list