[java-idp-plugin-oidc-rp] branch main updated: Add support for new authz/authn message encoders and handlers

Phil Smart philip.smart at jisc.ac.uk
Fri Jan 7 10:38:10 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=54c09c8b52790bdbc402f9c5acb28af67efcebf9

The following commit(s) were added to refs/heads/main by this push:
     new 54c09c8  Add support for new authz/authn message encoders and handlers
54c09c8 is described below

commit 54c09c8b52790bdbc402f9c5acb28af67efcebf9
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Jan 7 10:38:03 2022 +0000

    Add support for new authz/authn message encoders and handlers
    
    Some other cleanup
    Fix SWF flow test
    Make changes to support new OIDC.SSO profile classes
---
 .../authn/oidc/rp/context/OIDCAuthnContext.java    |  62 ++++
 .../context/OIDCAuthorizationRequestContext.java   |  11 -
 idp-oidc-rp-impl/pom.xml                           |  22 +-
 .../plugin/authn/oidc/rp/impl/AddAuthzRequest.java |  95 ++++++-
 .../oidc/rp/impl/AuthorizationController.java      | 186 +++++++-----
 ...OutboundAuthorizationRequestMessageContext.java |  22 +-
 .../rp/impl/InitializeRelyingPartyContext.java     |  28 +-
 .../authn/oidc/rp/impl/OIDCProxySupport.java       |  61 ++++
 .../META-INF/net.shibboleth.idp/postconfig.xml     |  19 +-
 .../oidc-relying-party-authn-beans.xml             |  46 ++-
 .../oidc-relying-party-authn-flow.xml              |  45 +--
 .../authn/providermetadata-resolver-system.xml     |   2 +-
 .../resources/templates/oidc-request-form-post.vm  |  46 +++
 .../oidc/rp/impl/AuthorizationControllerTest.java  | 311 +++++++++++++++++++++
 .../plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java  |  10 +-
 .../resources/conf/additional-system-beans.xml     |  28 ++
 16 files changed, 829 insertions(+), 165 deletions(-)

diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OIDCAuthnContext.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OIDCAuthnContext.java
new file mode 100644
index 0000000..76cb049
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OIDCAuthnContext.java
@@ -0,0 +1,62 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp.context;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.BaseContext;
+import org.opensaml.messaging.handler.MessageHandler;
+import org.opensaml.profile.action.ProfileAction;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Manages state during proxied OIDC authentication.
+ */
+public class OIDCAuthnContext extends BaseContext {
+    
+    /** Outbound message handler to run prior to encoding. */
+    @Nullable private MessageHandler outboundMessageHandler;
+    
+    /** Profile action to execute to produce outbound message response. */
+    @Nonnull private final ProfileAction encodeMessageAction;
+    
+    /**
+     * Constructor.
+     *
+     * @param action message-encoding profile action
+     */
+    public OIDCAuthnContext(@Nonnull final ProfileAction action) {
+        encodeMessageAction = Constraint.isNotNull(action, "Profile action cannot be null");
+    }
+    
+    /**
+     * Get the message-encoding profile action.
+     * 
+     * @return profile action
+     */
+    @Nonnull public ProfileAction getEncodeMessageAction() {
+        return encodeMessageAction;
+    }
+    
+    /**
+     * Get the outbound {@link MessageHandler} to run prior to encoding.
+     * 
+     * @return the outbound {@link MessageHandler}
+     */
+    @Nullable public MessageHandler getOutboundMessageHandler() {
+        return outboundMessageHandler;
+    }
+    
+    /**
+     * Set the outbound {@link MessageHandler} to run prior to encoding.
+     * 
+     * @param handler outbound {@link MessageHandler} to set
+     * 
+     * @return this context
+     */
+    @Nonnull public OIDCAuthnContext setOutboundMessageHandler(@Nullable final MessageHandler handler) {
+        outboundMessageHandler = handler;        
+        return this;
+    }
+
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/OIDCAuthorizationRequestContext.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/OIDCAuthorizationRequestContext.java
deleted file mode 100644
index a768639..0000000
--- a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/OIDCAuthorizationRequestContext.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context;
-
-import org.opensaml.messaging.context.BaseContext;
-
-/**
- * Subcontext carrying information to form an authorization request for an OpenID Connect Provider. This context
- * appears as a subcontext of the {@link org.opensaml.messaging.context.MessageContext}.
- */
-public class OIDCAuthorizationRequestContext extends BaseContext {
-
-}
diff --git a/idp-oidc-rp-impl/pom.xml b/idp-oidc-rp-impl/pom.xml
index 019f384..e4ecd8e 100644
--- a/idp-oidc-rp-impl/pom.xml
+++ b/idp-oidc-rp-impl/pom.xml
@@ -81,21 +81,16 @@
             <artifactId>oidc-common-profile-api</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>net.shibboleth.oidc</groupId>
+            <artifactId>oidc-common-profile-impl</artifactId>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
             <scope>provided</scope>
         </dependency>
-        <!-- Tmp OP deps until things move to commons -->
-      <!--    <dependency>
-            <groupId>net.shibboleth.idp.plugin.oidc</groupId>
-            <artifactId>idp-plugin-oidc-op-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>net.shibboleth.idp.plugin.oidc</groupId>
-            <artifactId>idp-plugin-oidc-op-impl</artifactId>
-        </dependency> -->
-        <!-- Service API and Plugin Description dependency -->
         <dependency>
             <groupId>${idp.groupId}</groupId>
             <artifactId>idp-admin-api</artifactId>
@@ -119,7 +114,7 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>net.shibboleth.idp</groupId>
+            <groupId>${idp.groupId}</groupId>
             <artifactId>idp-conf</artifactId>
             <scope>test</scope>
         </dependency>
@@ -128,6 +123,11 @@
             <artifactId>idp-conf-impl</artifactId>
             <scope>test</scope>
         </dependency>
+         <dependency>
+            <groupId>${opensaml.groupId}</groupId>
+            <artifactId>opensaml-messaging-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddAuthzRequest.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddAuthzRequest.java
index d82cb36..cfa5255 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddAuthzRequest.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddAuthzRequest.java
@@ -1,28 +1,71 @@
+/*
+ * 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.net.URI;
+import java.net.URISyntaxException;
+
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import org.opensaml.messaging.context.MessageContext;
 import org.opensaml.messaging.context.navigate.ParentContextLookup;
 import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.nimbusds.oauth2.sdk.ResponseType;
+import com.nimbusds.oauth2.sdk.id.ClientID;
+
 import net.shibboleth.idp.authn.AbstractAuthenticationAction;
 import net.shibboleth.idp.authn.context.AuthenticationContext;
 import net.shibboleth.idp.profile.IdPEventIds;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
-import net.shibboleth.idp.saml.saml2.profile.config.BrowserSSOProfileConfiguration;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+
 
+/**
+ * Action that creates an {@link OIDCAuthenticationRequest} and sets it as the message returned by
+ * {@link ProfileRequestContext#getOutboundMessageContext()}.
+ * 
+ * <p>Note, this is an OIDC authentication request on top of an OAuth 2.0 authorization request.</p>
+ * 
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link EventIds#INVALID_MSG_CTX}
+ * @event {@link EventIds#INVALID_PROFILE_CTX}
+ * @event {@link IdPEventIds#INVALID_PROFILE_CONFIG}
+ * 
+ * @post ProfileRequestContext.getOutboundMessageContext().getMessage() != null
+ */
 public class AddAuthzRequest extends AbstractAuthenticationAction {
     
     /** Class logger. */
-    @Nonnull private Logger log = LoggerFactory.getLogger(AddAuthzRequest.class);
+    @Nonnull private Logger log = LoggerFactory.getLogger(AddAuthzRequest.class);    
+    
+    /** Overwrite an existing message? */
+    private boolean overwriteExisting;
     
     /** Applicable profile configuration. */
-    //TODO not currently used - needs profile implementation actions to work
-    @Nullable private BrowserSSOProfileConfiguration profileConfiguration;
+    @Nullable private OIDCAuthorizationConfiguration profileConfiguration;
     
     /** Constructor.*/
     public AddAuthzRequest() {
@@ -30,6 +73,17 @@ public class AddAuthzRequest extends AbstractAuthenticationAction {
         setAuthenticationContextLookupStrategy(new ParentContextLookup<>(AuthenticationContext.class));
     }
     
+    /**
+     * Set whether to overwrite an existing message.
+     * 
+     * @param flag flag to set
+     */
+    public void setOverwriteExisting(final boolean flag) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        overwriteExisting = flag;
+    }
+    
     @Override
     protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
             @Nonnull final AuthenticationContext authenticationContext) {
@@ -40,15 +94,28 @@ public class AddAuthzRequest extends AbstractAuthenticationAction {
         
         final RelyingPartyContext rpCtx = profileRequestContext.getSubcontext(RelyingPartyContext.class);
         if (rpCtx != null && rpCtx.getConfiguration() != null &&
-                rpCtx.getProfileConfig() instanceof BrowserSSOProfileConfiguration) {
-            profileConfiguration = (BrowserSSOProfileConfiguration) rpCtx.getProfileConfig();
+                rpCtx.getProfileConfig() instanceof OIDCAuthorizationConfiguration) {
+            profileConfiguration = (OIDCAuthorizationConfiguration) rpCtx.getProfileConfig();
         }
         if (profileConfiguration == null) {
-            log.error("{} BrowserSSOProfileConfiguration not found", getLogPrefix());
+            log.error("{} OIDCCoreProtocolConfiguration not found", getLogPrefix());
             ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
             return false;
         }
         
+        final MessageContext outboundMessageCtx = profileRequestContext.getOutboundMessageContext();
+        if (outboundMessageCtx == null) {
+            log.debug("{} No outbound message context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+            return false;
+        } else if (!overwriteExisting && outboundMessageCtx.getMessage() != null) {
+            log.debug("{} Outbound message context already contains a message", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+            return false;
+        }
+        
+        outboundMessageCtx.setMessage(null);
+        
         return true;
     }
     
@@ -59,6 +126,20 @@ public class AddAuthzRequest extends AbstractAuthenticationAction {
         log.debug("{} Building AuthzRequest for upstream OP ({})", 
                 getLogPrefix(), authenticationContext.getAuthenticatingAuthority());
         
+        try {
+            final OIDCAuthenticationRequest request = new OIDCAuthenticationRequest(new ClientID("clientID"));
+            // FIXME: This needs to be dynamic, is a demo for now
+            request.setResponseType(ResponseType.CODE);
+            request.setEndpointURI(new URI("https://somewhere.com/oauth2/authz"));
+            request.setRedirectURI(new URI("https://localhost:8080/callback"));
+            
+            log.debug("{} Built authorization request for endpoint '{}'",getLogPrefix(), request.getEndpointURI());
+            profileRequestContext.getOutboundMessageContext().setMessage(request);
+        } catch (URISyntaxException e) {
+            log.error("{} Unable to create authorization request for downstream OP '{}'", getLogPrefix(), "OP");
+            
+        }
+        
     }
 
 }
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 49fcd41..d9a17f6 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
@@ -18,7 +18,7 @@
 package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
 
 import java.io.IOException;
-import java.net.URISyntaxException;
+import java.util.function.Function;
 
 import javax.annotation.Nonnull;
 import javax.annotation.concurrent.ThreadSafe;
@@ -27,6 +27,10 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.EventContext;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -34,11 +38,14 @@ import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 
+import com.nimbusds.oauth2.sdk.id.State;
+
 import net.shibboleth.idp.authn.ExternalAuthentication;
 import net.shibboleth.idp.authn.ExternalAuthenticationException;
 import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCAuthnContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
 
 /**
  * 
@@ -59,76 +66,101 @@ import net.shibboleth.utilities.java.support.primitive.StringSupport;
  * <p>
  * The {@link HttpServletRequest} is placed inside the {@link OpenIDConnectContext} for interrogation later in the flow.
  * </p>
- * 
- * @since 4.0.0
+ * FIXME: this description
  */
 @ThreadSafe
 @Controller
- at RequestMapping("/Authn/OIDC/RP")
+ at RequestMapping("%{shibboleth.authn.OIDC.externalAuthnPath:/Authn/OIDC/RP}")
 public class AuthorizationController {
-
-    /** Prefix for the session attribute ids. */
-    @Nonnull public static final String SESSION_ATTR_PREFIX =
-            "net.shibboleth.idp.authn.oidc.impl.OpenIdConnectStartServlet.";
-
-    /** Session attribute id for flow conversation key. */
-    @Nonnull public static final String SESSION_ATTR_FLOWKEY = SESSION_ATTR_PREFIX + "key";
-
-    /** Session attribute id for {@link OpenIDConnectContext}. */
-    @Nonnull public static final String SESSION_ATTR_SUCTX = SESSION_ATTR_PREFIX + "openIdConnectContext";
-
-    /** Serial UID. */
-    private static final long serialVersionUID = -3162157736238514852L;
-
+    
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(AuthorizationController.class);
+    
+    /** Lookup strategy to locate the nested ProfileRequestContext. */
+    @Nonnull private Function<ProfileRequestContext,ProfileRequestContext> profileRequestContextLookupStrategy;    
+    
+    /** Lookup strategy to locate the SAML context. */
+    @Nonnull private Function<ProfileRequestContext,OIDCAuthnContext> oidcContextLookupStrategy;
+    
+    /** Constructor.*/
+    public AuthorizationController() {
+        // PRC -> AC -> nested PRC
+        profileRequestContextLookupStrategy = new ChildContextLookup<>(ProfileRequestContext.class).compose(
+                new ChildContextLookup<>(AuthenticationContext.class));
+        
+        // PRC -> AC -> OIDCAuthnContext
+        oidcContextLookupStrategy = new ChildContextLookup<>(OIDCAuthnContext.class).compose(
+                new ChildContextLookup<>(AuthenticationContext.class));
+    }
 
 
     /**
-     * Begin an authorization request to the configured OP. 
-     * 
-     * TODO FINISH
+     * Begin an authorization request to the configured downstream OP. 
      * 
      * @param httpRequest the servlet request.
      * @param httpResponse the servlet response.
      * 
      * @throws ServletException throw if there is an error constructing an authz request.      
      * @throws IOException throw if there is an error constructing an authz request.
+     * @throws ExternalAuthenticationException 
      */
-    @GetMapping("/auth")
+    @GetMapping("/authz")
     public void authorizationRequest(@Nonnull final HttpServletRequest httpRequest, 
-            @Nonnull final HttpServletResponse httpResponse) throws ServletException, IOException {
-       
-        try {
-            final String key = ExternalAuthentication.startExternalAuthentication(httpRequest);
-            httpRequest.getSession().setAttribute(SESSION_ATTR_FLOWKEY, key);
-
-            @SuppressWarnings("rawtypes") final ProfileRequestContext profileRequestContext =
-                    (ProfileRequestContext) httpRequest.getAttribute(ProfileRequestContext.BINDING_KEY);
-            if (profileRequestContext == null) {
-                throw new ExternalAuthenticationException("Could not access profileRequestContext from the request");
-            }
-            final AuthenticationContext authenticationContext =
-                    profileRequestContext.getSubcontext(AuthenticationContext.class);
-            if (authenticationContext == null) {
-                throw new ExternalAuthenticationException("Could not get AuthenticationContext from the request");
-            }
-            final OpenIDConnectContext openIDConnectContext =
-                     authenticationContext
-                            .getSubcontext(OpenIDConnectContext.class);
-            if (openIDConnectContext == null) {
-                throw new ExternalAuthenticationException(
-                        "Could not get OpenIdConnectContext from the request");
+            @Nonnull final HttpServletResponse httpResponse) throws ServletException, IOException, ExternalAuthenticationException {
+        
+        final String key = ExternalAuthentication.startExternalAuthentication(httpRequest);
+        final ProfileRequestContext prc = ExternalAuthentication.getProfileRequestContext(key, httpRequest);
+        
+        final OIDCAuthnContext oidcContext = oidcContextLookupStrategy.apply(prc);
+        if (oidcContext == null) {
+            log.error("OIDCAuthnContext not found");
+            httpRequest.setAttribute(ExternalAuthentication.AUTHENTICATION_ERROR_KEY, EventIds.INVALID_PROFILE_CTX);
+            ExternalAuthentication.finishExternalAuthentication(key, httpRequest, httpResponse);
+            return;
+        }
+        
+        final ProfileRequestContext nestedPRC = profileRequestContextLookupStrategy.apply(prc);
+        if (nestedPRC == null) {
+            log.error("Nested ProfileRequestContext not found");
+            httpRequest.setAttribute(ExternalAuthentication.AUTHENTICATION_ERROR_KEY, EventIds.INVALID_PROFILE_CTX);
+            ExternalAuthentication.finishExternalAuthentication(key, httpRequest, httpResponse);
+            return;
+        }
+        
+        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));
+            
+        } else {
+            log.error("Outbound Authorization message not found");
+            httpRequest.setAttribute(ExternalAuthentication.AUTHENTICATION_ERROR_KEY, EventIds.INVALID_MESSAGE);
+            ExternalAuthentication.finishExternalAuthentication(key, httpRequest, httpResponse);
+            return;
+        }  
+        
+        try {            
+            if (oidcContext.getOutboundMessageHandler() != null) {
+                oidcContext.getOutboundMessageHandler().invoke(nestedPRC.getOutboundMessageContext());            }
+            
+            oidcContext.getEncodeMessageAction().execute(nestedPRC);
+            // Handle error added by the EncodeMessage action. 
+            final EventContext eventCtx = prc.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());
+                httpRequest.setAttribute(ExternalAuthentication.AUTHENTICATION_ERROR_KEY,
+                        eventCtx.getEvent().toString());
+                ExternalAuthentication.finishExternalAuthentication(key, httpRequest, httpResponse);
+                return;
             }
-            //TODO do not put state in session, put in the request.
-            httpRequest.getSession().setAttribute(SESSION_ATTR_SUCTX, openIDConnectContext);
-            log.debug("Redirecting http-agent to {}", openIDConnectContext.getAuthenticationRequestURI());
-            httpResponse.sendRedirect(openIDConnectContext.getAuthenticationRequestURI().toString());
-        } catch (final ExternalAuthenticationException e) {
-            log.error("Error processing external authentication request", e);           
-            throw new ServletException("Error processing external authentication request", e);
+        } catch (final MessageHandlerException e) {
+            log.error("Caught message handling exception", e);
+            httpRequest.setAttribute(ExternalAuthentication.AUTHENTICATION_ERROR_KEY, EventIds.MESSAGE_PROC_ERROR);
+            ExternalAuthentication.finishExternalAuthentication(key, httpRequest, httpResponse);
         }
-       
+             
     }
     
     /**
@@ -145,30 +177,30 @@ public class AuthorizationController {
             @Nonnull final HttpServletResponse httpResponse) throws ServletException, IOException {
        
         try {
-            final HttpSession session = httpRequest.getSession();
-            if (session == null) {
-                throw new ExternalAuthenticationException("No session exists, this URL shouldn't be called directly");
-            }
-            final String key = StringSupport.trimOrNull((String) httpRequest.getSession()
-                    .getAttribute(AuthorizationController.SESSION_ATTR_FLOWKEY));
-            if (key == null) {
-                throw new ExternalAuthenticationException(
-                        "Could not find value for " + AuthorizationController.SESSION_ATTR_FLOWKEY);
-            }
-            final OpenIDConnectContext openIDConnectContext =
-                    (OpenIDConnectContext) httpRequest.getSession()
-                            .getAttribute(AuthorizationController.SESSION_ATTR_SUCTX);
-            if (openIDConnectContext == null) {
-                throw new ExternalAuthenticationException(
-                        "Could not find value for " + AuthorizationController.SESSION_ATTR_SUCTX);
-            }
-            log.trace("Attempting URL {}?{}", httpRequest.getRequestURL(), httpRequest.getQueryString());
-            try {
-                openIDConnectContext.setAuthenticationResponseURI(httpRequest);
-            } catch (final URISyntaxException e) {
-                throw new ExternalAuthenticationException("Could not parse response URI", e);
-            }
-            ExternalAuthentication.finishExternalAuthentication(key, httpRequest, httpResponse);
+//            final HttpSession session = httpRequest.getSession();
+//            if (session == null) {
+//                throw new ExternalAuthenticationException("No session exists, this URL shouldn't be called directly");
+//            }
+//            final String key = StringSupport.trimOrNull((String) httpRequest.getSession()
+//                    .getAttribute(AuthorizationController.SESSION_ATTR_FLOWKEY));
+//            if (key == null) {
+//                throw new ExternalAuthenticationException(
+//                        "Could not find value for " + AuthorizationController.SESSION_ATTR_FLOWKEY);
+//            }
+//            final OpenIDConnectContext openIDConnectContext =
+//                    (OpenIDConnectContext) httpRequest.getSession()
+//                            .getAttribute(AuthorizationController.SESSION_ATTR_SUCTX);
+//            if (openIDConnectContext == null) {
+//                throw new ExternalAuthenticationException(
+//                        "Could not find value for " + AuthorizationController.SESSION_ATTR_SUCTX);
+//            }
+//            log.trace("Attempting URL {}?{}", httpRequest.getRequestURL(), httpRequest.getQueryString());
+//            try {
+//                openIDConnectContext.setAuthenticationResponseURI(httpRequest);
+//            } catch (final URISyntaxException e) {
+//                throw new ExternalAuthenticationException("Could not parse response URI", e);
+//            }
+            ExternalAuthentication.finishExternalAuthentication("key-wrong", httpRequest, httpResponse);
         } catch (final ExternalAuthenticationException e) {
             log.error("Could not finish the external authentication", e);          
             throw new ServletException("Error finishing the external authentication", e);
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOutboundAuthorizationRequestMessageContext.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOutboundAuthorizationRequestMessageContext.java
index ee7ac0a..2296d48 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOutboundAuthorizationRequestMessageContext.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOutboundAuthorizationRequestMessageContext.java
@@ -1,3 +1,20 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
 
 import java.util.function.Function;
@@ -13,14 +30,13 @@ import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.OIDCAuthorizationRequestContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.OIDCProviderMetadataContext;
 import net.shibboleth.idp.profile.AbstractProfileAction;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
-
+// FIXME: think of the logic
 public class InitializeOutboundAuthorizationRequestMessageContext extends AbstractProfileAction {
     
     /** Class logger. */
@@ -99,7 +115,7 @@ public class InitializeOutboundAuthorizationRequestMessageContext extends Abstra
         
         final MessageContext msgCtx = new MessageContext();
         profileRequestContext.setOutboundMessageContext(msgCtx);
-        msgCtx.addSubcontext(new OIDCAuthorizationRequestContext());
+        //msgCtx.addSubcontext(new OIDCAuthorizationRequestContext());
         log.debug("{} Initialized outbound message context", getLogPrefix());
 
 
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeRelyingPartyContext.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeRelyingPartyContext.java
index a2544b6..3d1376e 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeRelyingPartyContext.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeRelyingPartyContext.java
@@ -1,3 +1,20 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
 
 import java.util.function.Function;
@@ -22,6 +39,16 @@ import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
+/**
+ * Action that adds a {@link RelyingPartyContext} to the current {@link ProfileRequestContext} tree via a creation
+ * function.
+ * 
+ * @event {@link EventIds#INVALID_MSG_CTX}
+ * @event {@link EventIds#INVALID_PROFILE_CTX}
+ * @event {@link IdPEventIds#INVALID_RELYING_PARTY_CTX}
+ * @post ProfileRequestContext.getSubcontext(RelyingPartyContext.class) != null with relying party id set.
+ */
+// TODO: this is pretty much identical to that in the OP, should that be moved into commons?
 public class InitializeRelyingPartyContext extends AbstractProfileAction {
     
     /** Class logger. */
@@ -111,7 +138,6 @@ public class InitializeRelyingPartyContext extends AbstractProfileAction {
         log.debug("Attaching RelyingPartyContext for OP {}", issuerId);
         rpContext.setRelyingPartyId(issuerId);
         final OIDCProviderMetadataContext oidcContext = oidcProviderMetadataContextLookupStrategy.apply(profileRequestContext);
-        //TODO is this sufficient to set verified to true?  Yes as a verified reyling party if metadata attached
         if (oidcContext != null && oidcContext.getProviderInformation() != null
                 && issuerId.equals(oidcContext.getProviderInformation().getIssuer().getValue())) {
             log.debug("{} Setting the OP context to 'verified'", getLogPrefix());
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
new file mode 100644
index 0000000..c214c76
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCProxySupport.java
@@ -0,0 +1,61 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.security.SecureRandom;
+
+import javax.annotation.Nonnull;
+
+import org.apache.commons.codec.binary.Hex;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Support class for OIDC proxy related implementations.
+ */
+//TODO Duo has similar support class methods, maybe merge.
+public final class OIDCProxySupport {
+    
+    /** Private constructor.*/
+    private OIDCProxySupport() {
+        
+    }
+    
+    /**
+     * Generates a random identifier to be used as a nonce.
+     *  
+     * @param length the length of the parameter.
+     * 
+     * @return the randomly generated nonce value.
+     */
+    @Nonnull static String generateNonce(@Nonnull final Integer length) {
+        final SecureRandom secureRandom = new SecureRandom();
+        final StringBuilder sb = new StringBuilder();
+        while(sb.length() < length){
+            sb.append(Integer.toHexString(secureRandom.nextInt()));
+        }
+        return sb.toString().substring(0, length);
+    }
+        
+    
+    /**
+     * <p>Generate a state parameter from a nonce component and an execution key component.</p>
+     * 
+     * <p>The nonce is separated from the key by a dot e.g. {@literal <nonce>.<keyHex>}.</p>
+     * 
+     *  <p>The nonce is assumed to be already encoded in its transmission format e.g. Hex. The key is
+     *  hex encoded before it is combined with the nonce. The result is assumed URL encoded e.g. inside
+     *  the allowed set of URI characters or, no character in the state is from the URI reserved set.</p>
+     * 
+     * @param nonce the nonce component. 
+     * @param key the key component. The key is hex encoded before it is added to the generated state.
+     * 
+     * @return the combined state component.
+     */
+    @Nonnull 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");
+        
+        final String keyHex = Hex.encodeHexString(key.getBytes());
+        return nonce+"."+keyHex;
+    }
+
+}
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 a7d633b..ef18587 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
@@ -45,20 +45,23 @@
     
     <bean id="AbstractOIDCProfile" abstract="true"
         p:securityConfiguration-ref="%{idp.security.authn.oidc.rp.config:shibboleth.oidc.DefaultSecurityConfiguration}" />
-    
-    <bean id="OIDC.SSO" parent="AbstractOIDCProfile" lazy-init="true"
-        class="net.shibboleth.oidc.profile.config.OIDCCoreProtocolConfiguration"
+        
+    <bean id="AbstractOIDCSSOProfile" parent="AbstractOIDCProfile" abstract="true"
         p:issuer-ref="issuer"
+        p:tokenEndpointAuthMethods="%{idp.oidc.tokenEndpointAuthMethods:client_secret_basic,client_secret_post,client_secret_jwt,private_key_jwt}"
+        p:forcePKCE="%{idp.authn.oidc.rp.forcePKCE:false}"
+        p:allowPKCEPlain="%{idp.authn.oidc.rp.allowPKCEPlain:false}" 
         p:iDTokenLifetime="%{idp.authn.oidc.rp.idToken.defaultLifetime:PT1H}"
         p:accessTokenLifetime="%{idp.authn.oidc.rp.accessToken.defaultLifetime:PT10M}"
-        p:authorizeCodeLifetime="%{idp.authn.oidc.rp.authorizeCode.defaultLifetime:PT5M}"
         p:refreshTokenLifetime="%{idp.authn.oidc.rp.refreshToken.defaultLifetime:PT2H}"
-        p:tokenEndpointAuthMethods="%{idp.authn.oidc.rp.tokenEndpointAuthMethods:client_secret_basic,client_secret_post,client_secret_jwt,private_key_jwt}"
-        p:forcePKCE="%{idp.authn.oidc.rp.forcePKCE:false}"
-        p:allowPKCEPlain="%{idp.authn.oidc.rp.allowPKCEPlain:false}"
+        p:alwaysIncludedAttributes="%{idp.authn.oidc.rp.alwaysIncludedAttributes:}" />
+        
+    <bean id="OIDC.SSO" parent="AbstractOIDCSSOProfile" lazy-init="true"
+        class="net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration"
+        p:httpRequestMethod="%{idp.authn.oidc.rp.httpRequestMethod:GET}"
+        p:authorizeCodeLifetime="%{idp.authn.oidc.rp.authorizeCode.defaultLifetime:PT5M}"
         p:encodeConsentInTokens="%{idp.authn.oidc.rp.encodeConsentInTokens:false}"
         p:encodedAttributes="%{idp.authn.oidc.rp.encodedAttributes:%{idp.oidc.embeddedAttributes:}}"
-        p:alwaysIncludedAttributes="%{idp.authn.oidc.rp.alwaysIncludedAttributes:}"
         p:deniedUserInfoAttributes="%{idp.authn.oidc.rp.deniedUserInfoAttributes:}" />
    
     <!--
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 98c4cef..89c8eb9 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
@@ -26,8 +26,8 @@
     
     
     <!-- Spring controller to start the authentication request and recieve the response -->
-    <bean id="shibboleth.authn.OpenIDConnect.externalAuthnPath" class="java.lang.String"
-        c:_0="servletRelative:/Authn/OIDC/RP/auth">
+    <bean id="shibboleth.authn.OIDC.externalAuthnPath" class="java.lang.String"
+        c:_0="servletRelative:/Authn/OIDC/RP/authz">
     </bean>
 
     <!-- Parent beans for indirecting into nested PRC. -->
@@ -111,6 +111,42 @@
     <bean id="AddAuthzRequest" scope="prototype"
         class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AddAuthzRequest"
         p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"/>
+        
+     <!-- Message Encoder factory is a prototype to allow reuse of the encoders -->
+    <bean id="messageEncoderFactory"
+        class="net.shibboleth.oidc.profile.impl.AuthenticationRequestMessageEncoderFactory" scope="prototype"
+        c:encoders-ref="shibboleth.authn.oidc.rp.AuthenticationRequestEncoders" />
+
+    <!-- List must itself be a prototype so new encoders are created per request -->
+    <util:list id="shibboleth.authn.oidc.rp.AuthenticationRequestEncoders" scope="prototype">
+        <ref bean="OIDCAuthnRedirectRequestEncoder" />
+        <ref bean="OIDCAuthnPostRequestEncoder" />
+    </util:list>
+
+    <bean id="OIDCAuthnRedirectRequestEncoder"
+        class="net.shibboleth.oidc.profile.encoders.impl.HTTPRedirectEncoder" init-method="" scope="prototype"
+        p:httpServletResponse-ref="shibboleth.HttpServletResponse" />
+
+    <bean id="OIDCAuthnPostRequestEncoder" class="net.shibboleth.oidc.profile.encoders.impl.HTTPPostEncoder"
+        init-method="" scope="prototype" p:velocityEngine-ref="shibboleth.VelocityEngine"
+        p:httpServletResponse-ref="shibboleth.HttpServletResponse" />
+
+
+    <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>
+               
+            </list>
+        </property>
+    </bean>
+    
+    
+    
+    
     
     <!-- OLD STUFF -->
     
@@ -154,12 +190,12 @@
 	   class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateTokenClaims"
 	   p:cleanupHook="#{getObject('shibboleth.authn.oidc.rp.jwt.claims.CleanUpHook') 
 	       ?: getObject('shibboleth.authn.oidc.rp.jwt.claims.DefaultCleanupHook')}"
-	   p:claimsValidator="#{getObject('shibboleth.authn.oidc.rp.DuoTokenClaimsVerifier') 
-           ?: getObject('shibboleth.authn.oidc.rp.DefaultDuoTokenClaimsVerifier')}" />
+	   p:claimsValidator="#{getObject('shibboleth.authn.oidc.rp.RPTokenClaimsVerifier') 
+           ?: getObject('shibboleth.authn.oidc.rp.DefaultRPTokenClaimsVerifier')}" />
     
     <!-- TODO ensure these claims are correct in the general OIDC case. -->
      <!-- OIDC claims verification Other claim verifications e.g. ACR and AZP-->
-    <bean id="shibboleth.authn.oidc.rp.DefaultDuoTokenClaimsVerifier"
+    <bean id="shibboleth.authn.oidc.rp.DefaultRPTokenClaimsVerifier"
         class="net.shibboleth.oidc.security.jwt.claims.impl.ChainingJWTClaimsValidator">
         <property name="claimValidators">
             <list>
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 5d0d608..c58dde7 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
@@ -18,8 +18,7 @@
     
     <action-state id="PostDiscovery">
         <!-- Nest new PRC inside AC -->
-        <evaluate expression="InitializeProxyProfileRequestContext" />
-        
+        <evaluate expression="InitializeProxyProfileRequestContext" />        
         <evaluate expression="FlowStartPopulateAuditContext" />
         
         <!-- this is nice to get the rp out of the authenticating auth and setup a msg context
@@ -28,57 +27,35 @@
         
         <!--  <evaluate expression="SAMLProtocolAndRole" />  maybe we need an OIDC role selector here, to say this is an
         OP over the normal RP -->
-        <evaluate expression="OIDCMetadataLookup" />
-        
+        <evaluate expression="OIDCMetadataLookup" />       
          
-
         <evaluate expression="InitializeRelyingPartyContext" />
         <evaluate expression="SelectRelyingPartyConfiguration" />
+        <!--  <evaluate expression="PostLookupPopulateAuditContext" /> -->
         <evaluate expression="InitializeOutboundMessageContext" />
         <evaluate expression="SelectProfileConfiguration" />
         <evaluate expression="AddAuthzRequest"/>
-        
+        <!-- <evaluate expression="PostRequestPopulateAuditContext" />
+        <evaluate expression="WriteAuditLog" /> -->
+                
         <!-- 
-
-        <evaluate expression="PostLookupPopulateAuditContext" />
-        
-        
-        <evaluate expression="InitializeOutboundMessageContext" />
         <evaluate expression="InitializeMessageChannelSecurityContext" />
         <evaluate expression="PopulateBindingAndEndpointContexts" />
-
-        <evaluate expression="PopulateRequestSignatureSigningParameters" />
-        
-        <evaluate expression="AddAuthnRequest" />
-        <evaluate expression="PostRequestPopulateAuditContext" />
-        <evaluate expression="WriteAuditLog" /> -->
+        <evaluate expression="PopulateRequestSignatureSigningParameters" />        
+         -->
         <evaluate expression="'proceed'" />
         
         <transition on="proceed" to="AuthRequest" />
     </action-state>
-    
-    
-    
-    <!-- Discovery done -->
-<!-- 
-	<action-state id="SetRPUIInformation">
-        <evaluate expression="SetRPUIInformation" />
-        <evaluate expression="'proceed'" />
-        <transition on="proceed" to="SetOIDCInformation" /> 
-    </action-state>
-
-    <action-state id="SetOIDCInformation">
-        <evaluate expression="SetOIDCInformation" />
-        <evaluate expression="'proceed'" />
-        <transition on="proceed" to="ExternalTransfer" />
-    </action-state> -->
 
     <view-state id="AuthRequest"
-        view="externalRedirect:#{T(net.shibboleth.idp.authn.ExternalAuthentication).getExternalRedirect(flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.OpenIDConnect.externalAuthnPath'), flowExecutionContext.getKey().toString())}">
+        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>
             <evaluate
                 expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).addSubcontext(new net.shibboleth.idp.authn.context.ExternalAuthenticationContext(new net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl(false)), true).setFlowExecutionUrl(flowExecutionUrl + '&_eventId_proceed=1')" />
+            <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).addSubcontext(new net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCAuthnContext(EncodeMessage), true)" result="flowScope.oidcContext" />
+            <evaluate expression="flowScope.oidcContext.setOutboundMessageHandler(PreEncodeMessageHandler)" />
           </on-render>
         <transition to="ValidateResponse" />
     </view-state>
diff --git a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/providermetadata-resolver-system.xml b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/providermetadata-resolver-system.xml
index f4b918b..fe8410a 100644
--- a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/providermetadata-resolver-system.xml
+++ b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/providermetadata-resolver-system.xml
@@ -81,7 +81,7 @@
         class="net.shibboleth.oidc.metadata.cache.impl.DefaultOIDCProviderMetadataExpirationTimeStrategy"/>
     
     <bean id="shibboleth.oidc.rp.DefaultODICProviderSourceMetadataExpirationTimeStrategy" scope="prototype"
-        class="net.shibboleth.oidc.metadata.cache.impl.DefaultOIDCProviderSourceMetadataExpirationTimeStrategy"
+        class="net.shibboleth.oidc.metadata.cache.impl.DefaultSourceMetadataExpirationTimeStrategy"
         c:duration="PT10M" />
 
     <bean id="shibboleth.oidc.rp.DefaultOIDCProviderMetadataIdentifierExtractionStrategy" scope="prototype"
diff --git a/idp-oidc-rp-impl/src/main/resources/templates/oidc-request-form-post.vm b/idp-oidc-rp-impl/src/main/resources/templates/oidc-request-form-post.vm
new file mode 100644
index 0000000..8b303e0
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/resources/templates/oidc-request-form-post.vm
@@ -0,0 +1,46 @@
+##
+## Velocity Template for OIDC Form Post response mode.
+##
+##
+<!DOCTYPE html>
+<html>
+
+<head>
+    <meta charset="utf-8" />
+</head>
+
+<body onload="document.forms[0].submit()">
+    <noscript>
+        <p>
+            <strong>Note:</strong> Since your browser does not support JavaScript, you must press the Continue button once to proceed.
+        </p>
+    </noscript>
+
+    <form action="${action}" method="post">
+         <div>
+            #if($client_id)
+            <input type="hidden" name="client_id" value="${client_id}" />#end #if($scope)
+
+            <input type="hidden" name="scope" value="${scope}" />#end #if($response_type)
+
+            <input type="hidden" name="response_type" value="${response_type}" />#end #if($response_mode)
+
+            <input type="hidden" name="response_mode" value="${response_mode}" />#end #if($redirect_uri)
+
+            <input type="hidden" name="redirect_uri" value="${redirect_uri}" />#end #if($state)
+
+            <input type="hidden" name="state" value="${state}" />#end #if($prompt)
+
+            <input type="hidden" name="prompt" value="${prompt}" />#end #if($request)
+
+            <input type="hidden" name="request" value="${request}" />#end 
+        </div>
+        <noscript>
+            <div>
+                <input type="submit" value="Continue" />
+            </div>
+        </noscript>
+    </form>
+</body>
+
+</html>
\ No newline at end of file
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
new file mode 100644
index 0000000..03006ff
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationControllerTest.java
@@ -0,0 +1,311 @@
+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.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URLEncoder;
+
+import javax.annotation.Nonnull;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.mockito.Mockito;
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.encoder.MessageEncoder;
+import org.opensaml.messaging.encoder.MessageEncodingException;
+import org.opensaml.messaging.handler.MessageHandlerException;
+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.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.ServletContextAttributeExporter;
+import org.springframework.webflow.core.collection.LocalAttributeMap;
+import org.springframework.webflow.core.collection.MutableAttributeMap;
+import org.springframework.webflow.execution.FlowExecution;
+import org.springframework.webflow.execution.repository.FlowExecutionRepository;
+import org.springframework.webflow.execution.repository.support.CompositeFlowExecutionKey;
+import org.springframework.webflow.executor.FlowExecutorImpl;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.oauth2.sdk.ResponseType;
+import com.nimbusds.oauth2.sdk.id.ClientID;
+
+import net.shibboleth.idp.authn.AuthenticationFlowDescriptor;
+import net.shibboleth.idp.authn.ExternalAuthentication;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.ExternalAuthenticationContext;
+import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
+import net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCAuthnContext;
+import net.shibboleth.idp.plugin.authn.test.flow.mock.IdPPropertyConfigurer;
+import net.shibboleth.idp.session.IdPSession;
+import net.shibboleth.idp.session.context.SessionContext;
+import net.shibboleth.idp.ui.context.RelyingPartyUIContext;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration.OIDCHttpRequestMethod;
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.oidc.profile.encoders.impl.AbstractOIDCMessageEncoder;
+import net.shibboleth.oidc.profile.encoders.impl.OIDCMessageEncoder;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.net.HttpServletSupport;
+import net.shibboleth.utilities.java.support.net.URLBuilder;
+
+
+ at ContextConfiguration(classes = {AuthorizationController.class, IdPPropertyConfigurer.class})
+ at WebAppConfiguration
+ at TestPropertySource(properties = {"shibboleth.authn.OIDC.externalAuthnPath=/Authn/OIDC/RP",})
+public class AuthorizationControllerTest extends AbstractTestNGSpringContextTests {
+    
+    /** The web application context loaded by the test framework. */
+    @Nonnull @Autowired private WebApplicationContext webApplicationContext;
+    
+    /** The endpoint to redirect the user-agent to.*/
+    @Nonnull private final String ENDPOINT_URI = "https://op.example.com/";
+    
+    /** The redirect to direct the user-agent to after successful authentication. */
+    @Nonnull private final String REDIRECT_URI = "https://rp.example.com/callback";
+    
+    /** The mock MVC entry point for testing. */
+    @Nonnull private MockMvc mockMvc;
+    
+    /** The mock servlet context.*/
+    @Nonnull @Autowired private ServletContext servletContext;
+    
+    /** The mock http servlet response.*/
+    @Nonnull @Autowired private HttpServletResponse response;
+    
+    /** The mock http servlet request.*/
+    @Nonnull @Autowired private HttpServletRequest request;
+    
+    /**
+     * Setup. 
+     * 
+     * @throws Exception on error.
+     */
+    @BeforeMethod
+    public void setUp() throws Exception {
+        
+     
+        // check controller is instantiated.
+        final AuthorizationController controller = webApplicationContext.getBean(AuthorizationController.class);        
+        assertNotNull(controller);
+        assertNotNull(response);
+        assertNotNull(request);
+
+        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
+       
+        //add and mock attributes of the servlet context.
+        exportServletContextAttributes();
+    }
+    
+    @Test
+    public void testSuccessfulAuthorizeRequest() throws Exception {
+       
+        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"));
+    }
+    
+    /**
+     * Export the FlowExecutor to the servlet context with the correct set of configured contexts. Mimicking the
+     * IdP's configuration of the {@link ServletContextAttributeExporter}.
+     * 
+     * @throws Exception on error.
+     */
+    private void 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());
+
+        Mockito.when(mockFlowExecutor.getExecutionRepository()).thenReturn(mockFlowExecutionRepo);
+        final CompositeFlowExecutionKey key = new CompositeFlowExecutionKey("1", "1");
+        Mockito.when(mockFlowExecutionRepo.parseFlowExecutionKey("e1s1")).thenReturn(key);
+        Mockito.when(mockFlowExecutionRepo.getFlowExecution(key)).thenReturn(mockFlowExecution);
+        Mockito.when(mockFlowExecution.getConversationScope()).thenReturn(map);
+
+        // overwrites previous if set from previous method executions.
+        servletContext.setAttribute(ExternalAuthentication.SWF_KEY, mockFlowExecutor);
+    }
+    
+    /**
+     * Build a nested {@link ProfileRequestContext} by configuring a suitable context tree for external authentication e.g. a
+     * {@link AuthenticationContext} and {@link ExternalAuthenticationContext}. Nest the proxy PRC under the authentication
+     * context.
+     * 
+     * @return a profile request context.
+     * @throws Exception on error creating the duo client
+     */
+    @Nonnull private ProfileRequestContext buildProfileRequestContext() throws Exception {
+
+        // Add an outer root PRC.
+        final ProfileRequestContext rootPrc = new ProfileRequestContext();
+        final AuthenticationContext ac = new AuthenticationContext();
+        rootPrc.addSubcontext(ac);
+        // Add a nested proxy PRC under the authentication context.
+        final ProfileRequestContext prc = new ProfileRequestContext();
+        ac.addSubcontext(prc);
+        
+        final ExternalAuthenticationContext ec = new ExternalAuthenticationContext(new ExternalAuthenticationImpl());
+        ac.addSubcontext(ec);
+        
+        final MockRedirectEncoder encoder = new MockRedirectEncoder();
+        encoder.setHttpServletResponse(response);
+        final MockEncodeMessage encode = new MockEncodeMessage(encoder);
+        encode.setHttpServletResponse(response);
+        final OIDCAuthnContext oidcContext = new OIDCAuthnContext(encode);
+        
+        ac.addSubcontext(oidcContext);
+
+        // will redirect here once finished or in error.
+        ec.setFlowExecutionUrl("http://localhost/idp/profile/SSO&_eventId_proceed=1");       
+
+        final AuthenticationFlowDescriptor afd = new AuthenticationFlowDescriptor();
+        afd.setId("authn/OIDCRelyingParty");
+        ac.setAttemptedFlow(afd);
+        ac.addSubcontext(new RelyingPartyUIContext());
+        ac.setForceAuthn(false);
+
+        final SubjectCanonicalizationContext scc = new SubjectCanonicalizationContext();
+        scc.setPrincipalName("jdoe");
+        final SessionContext sc = new SessionContext();
+        final IdPSession idpSession = Mockito.mock(IdPSession.class);
+        Mockito.when(idpSession.getPrincipalName()).thenReturn("jdoe");
+        sc.setIdPSession(idpSession);
+        rootPrc.addSubcontext(sc);
+        rootPrc.addSubcontext(scc);
+        
+        // Create an add a build OIDCAuthenticationRequest, add to nested PRC outboundmessage
+        final OIDCAuthenticationRequest request = 
+                new OIDCAuthenticationRequest(new ClientID("test-client"));
+        request.setResponseType(ResponseType.CODE);
+        request.setEndpointURI(new URI(ENDPOINT_URI));
+        request.setRedirectURI(new URI(REDIRECT_URI));
+        
+        final MessageContext msgCtx = new MessageContext();
+        msgCtx.setMessage(request);
+        prc.setOutboundMessageContext(msgCtx);
+    
+        return rootPrc;
+    }
+    
+    private ExternalAuthenticationContext extractExternalAuthContext() {
+        final Object flowExecutorObject = servletContext.getAttribute(ExternalAuthentication.SWF_KEY);
+        assertTrue(flowExecutorObject instanceof FlowExecutorImpl);
+        
+        final Object prcObject = ((FlowExecutorImpl)flowExecutorObject).getExecutionRepository().
+                getFlowExecution(new CompositeFlowExecutionKey("1", "1")).
+                getConversationScope().get(ProfileRequestContext.BINDING_KEY);
+        assertTrue(prcObject instanceof ProfileRequestContext);
+       
+        final ExternalAuthenticationContext extContext = ((ProfileRequestContext)prcObject).
+                getSubcontext(AuthenticationContext.class).getSubcontext(ExternalAuthenticationContext.class);
+        assertNotNull(extContext);
+        
+        return extContext;
+    }
+    
+    /** Mock EncodeMessage profile action.*/
+    private class MockEncodeMessage extends AbstractProfileAction {
+        
+        
+        /** The message encoder to be returned by this factory. */
+        @Nonnull private MessageEncoder messageEncoder;
+        
+        /**
+         * 
+         * Constructor.
+         *
+         * @param encoder the encoder to use.
+         */
+        public MockEncodeMessage(final MessageEncoder encoder) {
+            messageEncoder = encoder;
+        }
+        
+        /** {@inheritDoc} */
+        @Override
+        protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {    
+            try {
+
+                if (!messageEncoder.isInitialized()) {
+                    messageEncoder.setMessageContext(profileRequestContext.getOutboundMessageContext());
+                    messageEncoder.initialize();
+                }                
+                messageEncoder.prepareContext();                
+                messageEncoder.encode();
+                
+            } catch (final MessageEncodingException | ComponentInitializationException e) {
+                ActionSupport.buildEvent(profileRequestContext, EventIds.UNABLE_TO_ENCODE);
+            } finally {
+                messageEncoder.destroy();
+            }
+        }
+    }
+    
+    private class MockRedirectEncoder extends AbstractOIDCMessageEncoder {
+
+        @Override
+        public boolean test(OIDCHttpRequestMethod t) {
+            return true;
+        }
+
+        @Override
+        protected void doEncode() throws MessageEncodingException {
+            try {
+                
+                final MessageContext messageContext = getMessageContext();
+                final OIDCAuthenticationRequest outboundMessage = (OIDCAuthenticationRequest)messageContext.getMessage();
+                
+    
+                URLBuilder urlBuilder = new URLBuilder(outboundMessage.getEndpointURI().toString());
+                serializeAuthorizationParamsToUrl(outboundMessage, urlBuilder);     
+                final String redirectURL = urlBuilder.buildURL();
+                
+                final HttpServletResponse response = getHttpServletResponse();
+                HttpServletSupport.addNoCacheHeaders(response);
+                HttpServletSupport.setUTF8Encoding(response);
+                HttpServletSupport.setContentType(response, "application/x-www-form-urlencoded");
+                response.sendRedirect(redirectURL);
+            } catch (final IOException e) {
+                throw new MessageEncodingException("Problem sending HTTP redirect", e);
+            }
+            
+        }
+
+        
+        
+    }
+    
+
+}
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 6eebef8..ee88be0 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
@@ -30,14 +30,10 @@ import com.nimbusds.oauth2.sdk.ParseException;
 import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
 
 import net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor;
-import net.shibboleth.ext.spring.service.ReloadableSpringService;
 import net.shibboleth.idp.plugin.authn.test.flow.AbstractAuthnXmlFlowExecutionTests;
 import net.shibboleth.idp.plugin.authn.test.flow.mock.MockFlowBuilder;
-import net.shibboleth.idp.relyingparty.RelyingPartyConfigurationResolver;
-import net.shibboleth.idp.relyingparty.impl.ReloadingRelyingPartyConfigurationResolver;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
-import net.shibboleth.utilities.java.support.service.ReloadableService;
 
 /** Test the OIDC relying party flow.*/
 public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
@@ -172,6 +168,8 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         loadBeanDefinitionsFromXmlFile(builderContext, new ClassPathResource("META-INF/net.shibboleth.idp/postconfig.xml"));
         
         loadBeanDefinitionsFromXmlFile(builderContext, new ClassPathResource("conf/test-relyingparty-resolver-service.xml"));
+        
+        loadBeanDefinitionsFromXmlFile(builderContext, new ClassPathResource("conf/additional-system-beans.xml"));
     }
     
     
@@ -208,8 +206,6 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
                     .put("opensamlProfileRequestContext", buildProfileRequestContext("authn/OIDCRelyingParty",false,true));
         updateFlowExecution(flowExecution);
         flowExecution.start(inputMap, externalContext);    
-        //TODO: only check it has ended, should check correct state - but it does not allow this
-        //find a way to check last state of an ended flow.
-        assertFlowExecutionEnded();
+        assertCurrentStateEquals("AuthRequest");
     }
 }
diff --git a/idp-oidc-rp-impl/src/test/resources/conf/additional-system-beans.xml b/idp-oidc-rp-impl/src/test/resources/conf/additional-system-beans.xml
new file mode 100644
index 0000000..09628b3
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/resources/conf/additional-system-beans.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:context="http://www.springframework.org/schema/context"
+    xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
+    xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+    default-init-method="initialize" default-destroy-method="destroy">
+
+    <!-- from externalBeans.xml -->
+    <bean id="shibboleth.VelocityEngine" class="net.shibboleth.ext.spring.velocity.VelocityEngineFactoryBean">
+        <property name="velocityProperties">
+            <props>
+                <prop key="resource.loaders">classpath, string</prop>
+                <prop key="resource.loader.classpath.class">
+                    org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
+                </prop>
+                <prop key="resource.loader.string.class">
+                    org.apache.velocity.runtime.resource.loader.StringResourceLoader
+                </prop>
+            </props>
+        </property>
+    </bean>
+
+
+</beans>
\ No newline at end of file

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


More information about the commits mailing list