[java-idp-plugin-oidc-rp] branch main updated: Add more tests, cleanup some code and JavaDoc

Phil Smart philip.smart at jisc.ac.uk
Wed Sep 28 14:54:33 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=529ab2287c3ded150c5834f983b49fdbef91831c

The following commit(s) were added to refs/heads/main by this push:
     new 529ab22  Add more tests, cleanup some code and JavaDoc
529ab22 is described below

commit 529ab2287c3ded150c5834f983b49fdbef91831c
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Wed Sep 28 15:54:28 2022 +0100

    Add more tests, cleanup some code and JavaDoc
---
 .../oidc/rp/context/AbstractOIDCEntityContext.java |  17 ++
 .../AddAuthenticationContextClassReferences.java   |   5 +-
 .../plugin/authn/oidc/rp/impl/AddEndpointURI.java  |  12 +-
 .../idp/plugin/authn/oidc/rp/impl/AddNonce.java    |   5 +-
 .../plugin/authn/oidc/rp/impl/AddRedirectURI.java  |   7 +-
 .../oidc/rp/impl/DefaultClaimMergingStrategy.java  |   3 +-
 .../authn/oidc/rp/impl/UserInfoEndpointLookup.java |   3 +-
 .../impl/OIDCClientMetadataLookupHandler.java      | 130 ------------
 .../impl/OIDCProviderMetadataLookupHandler.java    |  13 +-
 .../idp/service/relying-party/postconfig.xml       |  27 ++-
 .../authn/oidc/rp/impl/AbstractOIDCTest.java       |  28 +--
 ...ddAuthenticationContextClassReferencesTest.java |  99 +++++++++
 .../authn/oidc/rp/impl/AddEndpointURITest.java     |  91 ++++++++
 .../rp/impl/AddForceAuthenticationPromptTest.java  |  97 +++++++++
 .../authn/oidc/rp/impl/AddRedirectURITest.java     | 100 +++++++++
 .../plugin/authn/oidc/rp/impl/AddScopesTest.java   | 114 ++++++++++
 ...eOIDCSubjectIdentifierCanonicalizationTest.java | 111 ++++++++++
 .../oidc/rp/impl/UserInfoEndpointLookupTest.java   | 193 +++++++++++++++++
 .../oidc/rp/impl/ValidateResponseStateTest.java    | 119 +++++++++++
 .../OIDCProviderMetadataLookupHandlerTest.java     | 229 +++++++++++++++++++++
 .../resources/conf/authn/oidc-rp-credentials.xml   |  10 -
 21 files changed, 1227 insertions(+), 186 deletions(-)

diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/AbstractOIDCEntityContext.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/AbstractOIDCEntityContext.java
index 418d661..b58453f 100644
--- a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/AbstractOIDCEntityContext.java
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/AbstractOIDCEntityContext.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.context;
 
 import javax.annotation.Nullable;
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddAuthenticationContextClassReferences.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddAuthenticationContextClassReferences.java
index 5b9e7cd..666ad04 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddAuthenticationContextClassReferences.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddAuthenticationContextClassReferences.java
@@ -31,6 +31,7 @@ import org.slf4j.LoggerFactory;
 import com.nimbusds.openid.connect.sdk.claims.ACR;
 
 import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.config.navigate.ProxyAwareDefaultOIDCAuthenticationContextClassRequestLookupFunction;
 import net.shibboleth.oidc.authn.principal.AuthenticationContextClassReferencePrincipal;
 
 /** 
@@ -59,7 +60,9 @@ public class AddAuthenticationContextClassReferences extends AbstractOIDCAuthent
     /**
     * Build a list of {@link ACR}s if warranted. Converted from any default authentication method {@link Principal}s.
     * 
-    * <p>By default for this proxy case, the authentication methods are mapped from the upstream request.
+    * <p>By default for this proxy case, the authentication methods are mapped from the upstream request
+    * by the default authentication methods e.g. using the 
+    * {@link ProxyAwareDefaultOIDCAuthenticationContextClassRequestLookupFunction}.</p>
     * 
     * @param profileRequestContext current profile request context
     * 
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddEndpointURI.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddEndpointURI.java
index 0d058e3..3e4d715 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddEndpointURI.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddEndpointURI.java
@@ -19,15 +19,18 @@ package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
 
 import javax.annotation.Nonnull;
 
+import org.opensaml.profile.action.ActionSupport;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.oidc.profile.core.OidcEventIds;
 
 /** 
  * An action that adds the authorization endpoint URI from the providers metadata 
- * to the under constructions authentication request.
+ * to the under constructions authentication request. If an authorization endpoint does
+ * not exist, an error event is returned.
  */
 public class AddEndpointURI extends AbstractOIDCAuthenticationRequestAction {
     
@@ -38,6 +41,13 @@ public class AddEndpointURI extends AbstractOIDCAuthenticationRequestAction {
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
             @Nonnull final AuthenticationContext authenticationContext) {
         
+        if (getProviderMetadata().getAuthorizationEndpointURI() == null) {
+            log.error("{} OAuth 2.0 Authorization Endpoint URI not found in provider metadata", 
+                    getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_AUTHZ_ENDPOINT_URI);
+            return;
+        }
+        
         getAuthenticationRequest().setEndpointURI(getProviderMetadata().getAuthorizationEndpointURI());
         log.trace("{} Added authorization endpoint '{}' to authentication request for client '{}'",getLogPrefix(),
                 getAuthenticationRequest().getEndpointURI(), getAuthenticationRequest().getClientID());
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddNonce.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddNonce.java
index 2022bde..cfb6e16 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddNonce.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddNonce.java
@@ -32,14 +32,15 @@ import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
 /** 
- * An action that adds a nonce from a lookup strategy to the under constructions authentication request.
+ * An action that adds a nonce from a lookup strategy to the authentication request.
+ * The injected nonce generation strategy could generate a {@literal null} nonce.
  */
 public class AddNonce extends AbstractOIDCAuthenticationRequestAction {
     
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(AddNonce.class);
     
-    /** Strategy used to generate a nonce.*/
+    /** Strategy used to generate a nonce. Could generate a {@literal null} nonce.*/
     @Nonnull private Function<ProfileRequestContext, Nonce> nonceGenerationStrategy;
     
     /** Constructor.*/
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddRedirectURI.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddRedirectURI.java
index 99be73c..cafd3f9 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddRedirectURI.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddRedirectURI.java
@@ -41,8 +41,7 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
 
 /** 
  * Action that adds a redirect_uri to the authentication request.
- * 
- * TODO Events.*/
+ */
 public class AddRedirectURI extends AbstractOIDCAuthenticationRequestAction {
 
     /** Logger. */
@@ -105,11 +104,11 @@ public class AddRedirectURI extends AbstractOIDCAuthenticationRequestAction {
         final URI redirectUri = 
                 redirectUriCreationStrategy.apply(getHttpServletRequest(), profileRequestContext);
         if (redirectUri == null) {
-            log.error("{} Redirect URI could not be located or created from strategy", getLogPrefix());
+            log.error("{} Redirect URI could not be located or created using the strategy", getLogPrefix());
             ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_REDIRECT_URI);
             return;
         }
-        log.trace("Created redirect_uri '{}'", redirectUri);
+        log.trace("{} Created redirect_uri '{}'", getLogPrefix(), redirectUri);
         getAuthenticationRequest().setRedirectURI(redirectUri);
   
     }
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DefaultClaimMergingStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DefaultClaimMergingStrategy.java
index 0c98915..b294140 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DefaultClaimMergingStrategy.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DefaultClaimMergingStrategy.java
@@ -38,7 +38,6 @@ import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
  * is taken over that from the UserInfo response if the claim keys clash.</li>
  * </ol>
  */
-//TODO maybe this should filter claims as well e.g. exp etc. Security check this logic.
 public class DefaultClaimMergingStrategy implements BinaryOperator<ClaimsSet> {
     
     /** Class logger.*/
@@ -69,7 +68,7 @@ public class DefaultClaimMergingStrategy implements BinaryOperator<ClaimsSet> {
         // add UserInfo claims as a base
         final Map<String, Object> mergedClaimsMap = new HashMap<String, Object>(userInfoClaimsAsMap);
         
-        // Merge id_token claims into userInfo claims, take userinfo claim if conflict
+        // Merge id_token claims into userInfo claims, take id_token claim if conflict
         idTokenAsMap.forEach((key, value) ->
             mergedClaimsMap.merge(key, value, (v1, v2)-> {
                 log.trace("Claim '{}' exists in id_token and UserInfo response, taking id_token value '{}'",key, v2);
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/UserInfoEndpointLookup.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/UserInfoEndpointLookup.java
index 29370c5..462c9b8 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/UserInfoEndpointLookup.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/UserInfoEndpointLookup.java
@@ -87,8 +87,7 @@ public class UserInfoEndpointLookup extends AbstractHttpOIDCAuthenticationAction
           }
           
           try {              
-              final UserInfoResponse responseObject = handleRequest(profileRequestContext);
-              userInfoCtx.setUserInfo(responseObject);
+              userInfoCtx.setUserInfo(handleRequest(profileRequestContext));
           } catch (final OIDCRPException e) {
               log.error("{} Unable to return claims from UserInfo endpoint",getLogPrefix(),e);
               ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.AUTHN_EXCEPTION);
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCClientMetadataLookupHandler.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCClientMetadataLookupHandler.java
deleted file mode 100644
index f0ac8a9..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCClientMetadataLookupHandler.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * 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.metadata.impl;
-
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.messaging.context.MessageContext;
-import org.opensaml.messaging.handler.AbstractMessageHandler;
-import org.opensaml.messaging.handler.MessageHandlerException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.nimbusds.oauth2.sdk.id.Issuer;
-import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
-
-import net.shibboleth.oidc.metadata.ClientInformationResolver;
-import net.shibboleth.oidc.metadata.context.OIDCMetadataContext;
-import net.shibboleth.oidc.metadata.criterion.IssuerIDCriterion;
-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;
-import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
-import net.shibboleth.utilities.java.support.resolver.ResolverException;
-
-/**
- * Handler for inbound OIDC protocol messages that attempts to locate OIDC metadata for a RP, and attaches it with a
- * {@link OIDCMetadataContext} as a child of a pre-existing instance of {@link MessageContext}.
- */
-public class OIDCClientMetadataLookupHandler extends AbstractMessageHandler {
-
-    /** Logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(OIDCClientMetadataLookupHandler.class);
-
-    /** Resolver used to look up OIDC client information. */
-    @NonnullAfterInit private ClientInformationResolver clientResolver;
-
-    /** Strategy used to obtain the issuer value for the authorization request. */
-    @Nonnull
-    private Function<MessageContext, String> issuerLookupStrategy;
-
-    /**
-     * Constructor.
-     */
-    public OIDCClientMetadataLookupHandler() {
-        issuerLookupStrategy = new DefaultIssuerIDLookupFunction();
-    }
-
-    /**
-     * Set the strategy used to locate the issuer (downstream OP) for the request.
-     * 
-     * @param strategy lookup strategy
-     */
-    public void setIssuerLookupStrategy(@Nonnull final Function<MessageContext, String> strategy) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        issuerLookupStrategy =
-                Constraint.isNotNull(strategy, "IssuerLookupStrategy lookup strategy cannot be null");
-    }
-
-    /**
-     * Set the {@link ClientInformationResolver} to use.
-     * 
-     * @param resolver The resolver to use.
-     */
-    public void setClientInformationResolver(@Nonnull final ClientInformationResolver resolver) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
-        clientResolver = Constraint.isNotNull(resolver, "ClientInformationResolver cannot be null");
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-
-        if (clientResolver == null) {
-            throw new ComponentInitializationException("ClientInformationResolver cannot be null");
-        }
-    }
-
-    //TODO test if no client returned
-    /** {@inheritDoc} */
-    @Override
-    protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-        // Resolve issuer from inbound message
-        final String issuerString = issuerLookupStrategy.apply(messageContext);
-        if (issuerString == null) {
-            log.warn("{} Issuer not found, client information can not be resolved", getLogPrefix());
-            return;
-        }
-        // Resolve client metadata for given issuer
-        final IssuerIDCriterion issuerCriterion = new IssuerIDCriterion(new Issuer(issuerString));
-        final CriteriaSet criteria = new CriteriaSet(issuerCriterion);
-        try {
-            final OIDCClientInformation clientInformation = clientResolver.resolveSingle(criteria);
-            if (clientInformation == null) {
-                log.debug("{} No client information returned for issuer {}", getLogPrefix(), issuerString);
-                return;
-            }
-            log.debug("{} Resolved client information for client '{}'", getLogPrefix(), clientInformation.getID());
-            final OIDCMetadataContext oidcCtx = new OIDCMetadataContext();
-            oidcCtx.setClientInformation(clientInformation);
-            messageContext.addSubcontext(oidcCtx);
-            // Based on that info we know 1) client is valid 2) we know valid
-            // redirect uris
-            log.debug("{} {} added to MessageContext as child of {}", getLogPrefix(),
-                    OIDCMetadataContext.class.getName(), messageContext.getClass().getName());
-        } catch (final ResolverException e) {
-            log.error("{} ResolverException thrown during client information lookup", getLogPrefix(), e);
-        }
-    }
-}
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCProviderMetadataLookupHandler.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCProviderMetadataLookupHandler.java
index 5a161bb..8aeff92 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCProviderMetadataLookupHandler.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCProviderMetadataLookupHandler.java
@@ -112,7 +112,7 @@ public class OIDCProviderMetadataLookupHandler extends AbstractMessageHandler {
        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
 
-       providerResolver = Constraint.isNotNull(resolver, "IssuerMetadataResolver cannot be null");
+       providerResolver = Constraint.isNotNull(resolver, "ProviderMetadataResolver cannot be null");
    }
    
    /** {@inheritDoc} */
@@ -121,7 +121,7 @@ public class OIDCProviderMetadataLookupHandler extends AbstractMessageHandler {
        super.doInitialize();
 
        if (providerResolver == null) {
-           throw new ComponentInitializationException("IssuerMetadataResolver cannot be null");
+           throw new ComponentInitializationException("ProviderMetadataResolver cannot be null");
        }
 
    }
@@ -161,7 +161,9 @@ public class OIDCProviderMetadataLookupHandler extends AbstractMessageHandler {
             context.setProviderInformation(issuerMetadata);            
             entityCtx.addSubcontext(context);
         } catch (final ResolverException e) {
-            log.error("{} ResolverException thrown during provider metadata lookup", getLogPrefix(), e);
+            log.error("{} ResolverException thrown during provider metadata lookup: {}", 
+                    getLogPrefix(), e.getMessage());
+            throw new MessageHandlerException(e);
         }
     }
     
@@ -198,9 +200,10 @@ public class OIDCProviderMetadataLookupHandler extends AbstractMessageHandler {
                 }
                 log.debug("{} Existing OIDCProviderMetadataContext was resolved, but the issuer "
                         + "did not match the entity context data", getLogPrefix());
-            }
-            log.debug("{} Existing OIDCProviderMetadataContext was resolved, but was missing ProviderInformation "
+            } else {
+                log.debug("{} Existing OIDCProviderMetadataContext was resolved but is missing ProviderInformation "
                     + "data", getLogPrefix());
+            }
         } else {
             log.debug("{} No existing OIDCProviderMetadataContext was resolved", getLogPrefix());
         }
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
index e4eb51d..296508f 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
@@ -176,25 +176,21 @@
             </list>
         </property>
     </bean>
-
-    <bean id="shibboleth.authn.oidc.rp.SigningCredentials"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.config.CredentialsListFactory"
-        c:_0="#{getObject('shibboleth.authn.oidc.rp.DefaultSigningCredentials')}" />
-
-    <bean id="shibboleth.authn.oidc.rp.RequestObjectKeyTransportEncryptionCredentials"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.config.CredentialsListFactory"
-        c:_0="#{getObject('shibboleth.authn.oidc.rp.DefaultRequestObjectKeyTransportEncryptionCredentials')}" />
     
-    <bean id="shibboleth.authn.oidc.rp.RequestObjectDataEncryptionCredentials"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.config.CredentialsListFactory"
-        c:_0="#{getObject('shibboleth.authn.oidc.rp.DefaultRequestObjectDataEncryptionCredentials')}" />
-
     <bean id="shibboleth.authn.oidc.rp.DefaultJWTDecryptionConfiguration"
         parent="shibboleth.authn.oidc.rp.BasicDecryptionConfiguration"
         p:KEKCredentialResolver-ref="defaultOIDCRPKeyDecryptionCredentialResolver"
         p:contentEncryptionKeyCredentialResolver-ref="defaultOIDCRPContentDecryptionKeyCredentialResolver" />
+        
+        
+    <bean id="shibboleth.authn.oidc.rp.SigningCredentials"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.config.CredentialsListFactory"
+        c:_0="#{getObject('shibboleth.authn.oidc.rp.DefaultSigningCredentials')}" />
+        
 
-    <!-- A resolver to public/private key encryption keys global to the RP -->
+    <!-- 
+        A resolver to public/private key encryption keys global to the RP 
+    -->
     <bean id="defaultOIDCRPKeyDecryptionCredentialResolver"
         class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
         <constructor-arg>
@@ -213,7 +209,8 @@
     </bean>
 
     <!-- 
-        A resolver to resolve a pre-shared Direct Encryption key e.g. a pairwise client_secret from the input criterion 
+        A resolver to resolve a pre-shared Direct Encryption key e.g. a pairwise client_secret from the input criterion
+        from which to derive the decryption key. 
     -->
     <bean id="defaultOIDCRPContentDecryptionKeyCredentialResolver"
         class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
@@ -243,7 +240,7 @@
     <!-- 
         A resolver to resolve pre-shared Direct Encryption or Key Wrapping credentials based on the
         client_secret in the input criterion. Returns the raw value, even though the key to use
-        is a derivative of this key
+        is a derivative of this key.
     -->
     <bean id="defaultOIDCRPContentEncryptionKeyCredentialResolver"
         class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractOIDCTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractOIDCTest.java
index 80e68b1..e207d2e 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractOIDCTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractOIDCTest.java
@@ -119,13 +119,15 @@ public abstract class AbstractOIDCTest {
     
     /** Mock response from the UserInfo endpoint.*/
     @Nonnull @NotEmpty
-    protected final String USERINFO_RESPONSE ="{sub=user-subject-1234531, "
-            + "website=https://openid.net/, "
-            + "zoneinfo=America/Los_Angeles, "
-            + "birthdate=2000-02-03, gender=female, "
-            + "preferred_username=d.tu, "
-            + "given_name=Demo, middle_name=Theresa, locale=en-US, "
-            + "updated_at=1580000000, name=Demo T. User, nickname=Dee, family_name=User}";
+    protected final String USERINFO_RESPONSE ="{\n"
+            + "   \"sub\": \"248289761001\",\n"
+            + "   \"name\": \"Jane Doe\",\n"
+            + "   \"given_name\": \"Jane\",\n"
+            + "   \"family_name\": \"Doe\",\n"
+            + "   \"preferred_username\": \"j.doe\",\n"
+            + "   \"email\": \"janedoe at example.com\",\n"
+            + "   \"picture\": \"http://example.com/janedoe/me.jpg\"\n"
+            + "  }";
     
     /** Client metadata.*/
     @Nonnull protected final String CLIENT_METADATA = "[\n"
@@ -165,6 +167,9 @@ public abstract class AbstractOIDCTest {
     /** The RP config.*/
     protected OIDCAuthorizationConfiguration partyConfig;
     
+    /** The OIDC provider metadata context.*/
+    protected OIDCProviderMetadataContext providerCtx;
+    
     /** 
      * Setup the various contexts.
      * 
@@ -197,12 +202,7 @@ public abstract class AbstractOIDCTest {
         outMsgCtx.setMessage(authnRequest);
         prc.setOutboundMessageContext(outMsgCtx);      
 
-        // FIXME we should no longer need this context
         final JSONParser parser = new JSONParser(JSONParser.MODE_JSON_SIMPLE);
-        final OIDCMetadataContext metadataContext = new OIDCMetadataContext();
-        metadataContext.setClientInformation(
-                OIDCClientInformation.parse((JSONObject)((JSONArray)parser.parse(CLIENT_METADATA)).get(0)));
-        outMsgCtx.addSubcontext(metadataContext);
         
         final RelyingPartyContext partyContext = new RelyingPartyContext();
         partyConfig = new OIDCAuthorizationConfiguration();  
@@ -217,13 +217,13 @@ public abstract class AbstractOIDCTest {
         prc.addSubcontext(partyContext);
         
         peerEntityCtx = outMsgCtx.getSubcontext(OIDCPeerEntityContext.class,true);
-        final OIDCProviderMetadataContext providerCtx = new OIDCProviderMetadataContext();
+        providerCtx = new OIDCProviderMetadataContext();
         providerCtx.setProviderInformation(
                         OIDCProviderMetadata.parse((JSONObject)parser.parse(GOOD_PROVIDER_CONFIGURATION_INFO)));
         peerEntityCtx.addSubcontext(providerCtx);     
         outMsgCtx.addSubcontext(peerEntityCtx);
         
-        //Set the inbound reponse.        
+        //Set the inbound response.        
         final MessageContext inMsgCtx = new MessageContext();
         inMsgCtx.setMessage(AuthenticationResponseParser.parse(
                 new URI("/idp/profile/Authn/OIDC/RP/callback"
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddAuthenticationContextClassReferencesTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddAuthenticationContextClassReferencesTest.java
new file mode 100644
index 0000000..dd801ab
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddAuthenticationContextClassReferencesTest.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.context.navigate.ParentContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+import net.shibboleth.idp.relyingparty.RelyingPartyConfiguration;
+import net.shibboleth.oidc.authn.principal.AuthenticationContextClassReferencePrincipal;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+
+/** Tests for AddAuthenticationContextClassReferences.*/
+public class AddAuthenticationContextClassReferencesTest  extends AbstractOIDCTest {
+    
+    /** The action to test.*/
+    private AddAuthenticationContextClassReferences action;
+    
+    /** The RPC.*/
+    private RelyingPartyContext rpc;
+    
+    /** The profile config.*/
+    private OIDCAuthorizationConfiguration oidcAuthzConfig;
+    
+    @Override
+    @BeforeMethod
+    public void setup() throws Exception {
+        super.setup();
+        action = new AddAuthenticationContextClassReferences(); 
+        
+        rpc = prc.getSubcontext(RelyingPartyContext.class, true); 
+        oidcAuthzConfig = new OIDCAuthorizationConfiguration();
+        final RelyingPartyConfiguration rpConfig = new RelyingPartyConfiguration();
+        rpc.setProfileConfig(oidcAuthzConfig);
+        rpc.setConfiguration(rpConfig);
+        
+        action.setProfileContextLookupStrategy(new ChildContextLookup<>(ProfileRequestContext.class).compose(
+                new ChildContextLookup<>(AuthenticationContext.class)
+                .compose(new WebflowRequestContextProfileRequestContextLookup())));     
+        
+        action.setAuthenticationContextLookupStrategy(new ParentContextLookup<>(AuthenticationContext.class));
+        
+        oidcAuthzConfig.setDefaultAuthenticationMethods(List.of(
+                new AuthenticationContextClassReferencePrincipal("test-value-1"),
+                new AuthenticationContextClassReferencePrincipal("test-value-2"),
+                new AuthenticationContextClassReferencePrincipal("test-value-3")));
+        
+    }
+    
+    @Test
+    public void testSuccess() throws Exception {
+        action.initialize();
+        
+        action.execute(src);
+        
+        assertEquals(authnRequest.getAcrs().size(),3);
+        assertEquals(authnRequest.getAcrs().get(0).getValue(),"test-value-1");
+        assertEquals(authnRequest.getAcrs().get(1).getValue(),"test-value-2");
+        assertEquals(authnRequest.getAcrs().get(2).getValue(),"test-value-3");
+    }
+    
+    @Test
+    public void testSuccess_NoPrincipals() throws Exception {
+        oidcAuthzConfig.setDefaultAuthenticationMethods(Collections.emptyList());
+        
+        action.initialize();
+        
+        action.execute(src);
+        
+        assertEquals(authnRequest.getAcrs().size(),0);
+
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddEndpointURITest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddEndpointURITest.java
new file mode 100644
index 0000000..b77e310
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddEndpointURITest.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import static org.testng.Assert.assertEquals;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.context.navigate.ParentContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.webflow.execution.Event;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+import net.shibboleth.idp.relyingparty.RelyingPartyConfiguration;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+import net.shibboleth.oidc.profile.core.OidcEventIds;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+/** Tests for {@link AddEndpointURI}.*/
+public class AddEndpointURITest extends AbstractOIDCTest {
+    
+    /** The action to test.*/
+    private AddEndpointURI action;
+    
+    /** The RPC.*/
+    private RelyingPartyContext rpc;
+    
+    /** The profile config.*/
+    private OIDCAuthorizationConfiguration oidcAuthzConfig;
+    
+    @Override
+    @BeforeMethod
+    public void setup() throws Exception {
+        super.setup();
+        action = new AddEndpointURI(); 
+        
+        rpc = prc.getSubcontext(RelyingPartyContext.class, true); 
+        oidcAuthzConfig = new OIDCAuthorizationConfiguration();
+        final RelyingPartyConfiguration rpConfig = new RelyingPartyConfiguration();
+        rpc.setProfileConfig(oidcAuthzConfig);
+        rpc.setConfiguration(rpConfig);
+        
+        action.setProfileContextLookupStrategy(new ChildContextLookup<>(ProfileRequestContext.class).compose(
+                new ChildContextLookup<>(AuthenticationContext.class)
+                .compose(new WebflowRequestContextProfileRequestContextLookup())));     
+        
+        action.setAuthenticationContextLookupStrategy(new ParentContextLookup<>(AuthenticationContext.class));
+        
+    }
+    
+    @Test
+    public void testSuccess() throws ComponentInitializationException {
+        action.initialize();
+        
+        action.execute(src);
+        
+        assertEquals(authnRequest.getEndpointURI(), 
+                providerCtx.getProviderInformation().getAuthorizationEndpointURI());
+    }
+    
+    @Test
+    public void testNoAuthzEndpoint() throws ComponentInitializationException {
+        
+        providerCtx.getProviderInformation().setAuthorizationEndpointURI(null);
+        
+        action.initialize();
+        
+        final Event event = action.execute(src);
+        assertEquals(event.getId(),OidcEventIds.INVALID_AUTHZ_ENDPOINT_URI);
+
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddForceAuthenticationPromptTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddForceAuthenticationPromptTest.java
new file mode 100644
index 0000000..558a821
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddForceAuthenticationPromptTest.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.context.navigate.ParentContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.openid.connect.sdk.Prompt;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+import net.shibboleth.idp.relyingparty.RelyingPartyConfiguration;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+/** Tests for {@link AddForceAuthenticationPrompt}.*/
+public class AddForceAuthenticationPromptTest extends AbstractOIDCTest {
+    
+    /** The action to test.*/
+    private AddForceAuthenticationPrompt action;
+    
+    /** The RPC.*/
+    private RelyingPartyContext rpc;
+    
+    /** The profile config.*/
+    private OIDCAuthorizationConfiguration oidcAuthzConfig;
+    
+    @Override
+    @BeforeMethod
+    public void setup() throws Exception {
+        super.setup();
+        action = new AddForceAuthenticationPrompt(); 
+        
+        rpc = prc.getSubcontext(RelyingPartyContext.class, true); 
+        oidcAuthzConfig = new OIDCAuthorizationConfiguration();
+        final RelyingPartyConfiguration rpConfig = new RelyingPartyConfiguration();
+        rpc.setProfileConfig(oidcAuthzConfig);
+        rpc.setConfiguration(rpConfig);
+        
+        action.setProfileContextLookupStrategy(new ChildContextLookup<>(ProfileRequestContext.class).compose(
+                new ChildContextLookup<>(AuthenticationContext.class)
+                .compose(new WebflowRequestContextProfileRequestContextLookup())));     
+        
+        action.setAuthenticationContextLookupStrategy(new ParentContextLookup<>(AuthenticationContext.class));
+        
+    }
+    
+    @Test
+    public void testSuccess() throws ComponentInitializationException {
+        
+        oidcAuthzConfig.setForceAuthn(true);
+        
+        action.initialize();
+        
+        action.execute(src);
+        
+        assertEquals(authnRequest.getPrompt().size(), 1);
+        assertEquals(authnRequest.getPrompt().iterator().next(), Prompt.Type.LOGIN);
+    }
+    
+    @Test
+    public void testForceAuthnNotRequired() throws ComponentInitializationException {
+        
+        oidcAuthzConfig.setForceAuthn(false);
+        
+        action.initialize();
+        
+        action.execute(src);
+        
+        assertNull(authnRequest.getPrompt());
+
+
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddRedirectURITest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddRedirectURITest.java
new file mode 100644
index 0000000..f592387
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddRedirectURITest.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.context.navigate.ParentContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.webflow.execution.Event;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+import net.shibboleth.idp.relyingparty.RelyingPartyConfiguration;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+import net.shibboleth.oidc.profile.core.OidcEventIds;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+/** Tests for {@link AddRedirectURI}.*/
+public class AddRedirectURITest extends AbstractOIDCTest {
+    
+    /** The action to test.*/
+    private AddRedirectURI action;
+    
+    /** The RPC.*/
+    private RelyingPartyContext rpc;
+    
+    /** The profile config.*/
+    private OIDCAuthorizationConfiguration oidcAuthzConfig;
+    
+    @Override
+    @BeforeMethod
+    public void setup() throws Exception {
+        super.setup();
+        action = new AddRedirectURI(); 
+        
+        rpc = prc.getSubcontext(RelyingPartyContext.class, true); 
+        oidcAuthzConfig = new OIDCAuthorizationConfiguration();
+        final RelyingPartyConfiguration rpConfig = new RelyingPartyConfiguration();
+        rpc.setProfileConfig(oidcAuthzConfig);
+        rpc.setConfiguration(rpConfig);
+        
+        action.setProfileContextLookupStrategy(new ChildContextLookup<>(ProfileRequestContext.class).compose(
+                new ChildContextLookup<>(AuthenticationContext.class)
+                .compose(new WebflowRequestContextProfileRequestContextLookup())));     
+        
+        action.setAuthenticationContextLookupStrategy(new ParentContextLookup<>(AuthenticationContext.class));
+        
+    }
+    
+    @Test
+    public void testSuccess() throws ComponentInitializationException, URISyntaxException {
+        
+        final URI redirectUri = new URI("https://rp.example.com/callback");
+        
+        action.setRedirectUriCreationStrategy((http, prc) -> redirectUri);
+        
+        action.initialize();
+        
+        action.execute(src);
+        
+        assertEquals(authnRequest.getRedirectURI(),redirectUri);
+    }
+    
+    @Test
+    public void testNullURI() throws ComponentInitializationException, URISyntaxException {        
+        
+        action.setRedirectUriCreationStrategy((http, prc) -> null);
+        
+        action.initialize();
+        
+        final Event event = action.execute(src);
+        
+        assertEquals(event.getId(),  OidcEventIds.INVALID_REDIRECT_URI);
+    }
+
+
+}
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddScopesTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddScopesTest.java
new file mode 100644
index 0000000..309d5f7
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddScopesTest.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.net.URISyntaxException;
+import java.util.Set;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.context.navigate.ParentContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+import net.shibboleth.idp.relyingparty.RelyingPartyConfiguration;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+/** Tests for {@link AddScopes}.*/
+public class AddScopesTest extends AbstractOIDCTest {
+    
+    /** The action to test.*/
+    private AddScopes action;
+    
+    /** The RPC.*/
+    private RelyingPartyContext rpc;
+    
+    /** The profile config.*/
+    private OIDCAuthorizationConfiguration oidcAuthzConfig;
+    
+    @Override
+    @BeforeMethod
+    public void setup() throws Exception {
+        super.setup();
+        action = new AddScopes(); 
+        
+        rpc = prc.getSubcontext(RelyingPartyContext.class, true); 
+        oidcAuthzConfig = new OIDCAuthorizationConfiguration();
+        final RelyingPartyConfiguration rpConfig = new RelyingPartyConfiguration();
+        rpc.setProfileConfig(oidcAuthzConfig);
+        rpc.setConfiguration(rpConfig);
+        
+        action.setProfileContextLookupStrategy(new ChildContextLookup<>(ProfileRequestContext.class).compose(
+                new ChildContextLookup<>(AuthenticationContext.class)
+                .compose(new WebflowRequestContextProfileRequestContextLookup())));     
+        
+        action.setAuthenticationContextLookupStrategy(new ParentContextLookup<>(AuthenticationContext.class));
+        
+    }
+    
+    @Test
+    public void testSuccessWithDefaultScope() throws ComponentInitializationException, URISyntaxException {        
+      
+        action.initialize();        
+        action.execute(src);
+        
+        // Always one more scope as openid is default
+        assertEquals(authnRequest.getScope().size(), 1);
+        assertEquals(authnRequest.getScope().iterator().next().getValue(), "openid");
+    }
+    
+    @Test
+    public void testSuccess_AddScopes() throws ComponentInitializationException, URISyntaxException {
+        
+        oidcAuthzConfig.setScopes(Set.of("profile","email"));
+        
+        action.initialize();        
+        action.execute(src);
+        
+        // Always one more scope as openid is default
+        assertEquals(authnRequest.getScope().size(), 3);
+        assertTrue(authnRequest.getScope().contains("openid"));
+        assertTrue(authnRequest.getScope().contains("profile"));
+        assertTrue(authnRequest.getScope().contains("email"));
+        
+    }
+    
+    @Test
+    public void testSuccess_NullScopes() throws ComponentInitializationException, URISyntaxException {
+        
+        oidcAuthzConfig.setScopes(null);
+        
+        action.initialize();        
+        action.execute(src);
+        
+        // Always one more scope as openid is default
+        assertEquals(authnRequest.getScope().size(), 1);
+        assertTrue(authnRequest.getScope().contains("openid"));
+        
+    }
+
+
+
+}
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SimpleOIDCSubjectIdentifierCanonicalizationTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SimpleOIDCSubjectIdentifierCanonicalizationTest.java
new file mode 100644
index 0000000..31775ba
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SimpleOIDCSubjectIdentifierCanonicalizationTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.Arrays;
+
+import javax.security.auth.Subject;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.webflow.execution.Event;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.principal.OIDCSubjectIdentifierPrincipal;
+import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+import net.shibboleth.idp.profile.testing.ActionTestingSupport;
+import net.shibboleth.utilities.java.support.collection.Pair;
+
+/** Tests for {@link SimpleOIDCSubjectIdentifierCanonicalization}.*/
+public class SimpleOIDCSubjectIdentifierCanonicalizationTest extends AbstractOIDCTest {
+    
+    private SimpleOIDCSubjectIdentifierCanonicalization action;
+    
+    @Override
+    @BeforeMethod
+    public void setup() throws Exception {
+        super.setup();
+        action = new SimpleOIDCSubjectIdentifierCanonicalization();
+        action.setTransforms(Arrays.asList(new Pair<>("^(.+)@osu\\.edu$", "$1")));
+        action.setProfileContextLookupStrategy(new ChildContextLookup<>(ProfileRequestContext.class).compose(
+                new ChildContextLookup<>(AuthenticationContext.class)
+                .compose(new WebflowRequestContextProfileRequestContextLookup())));     
+        action.setActivationCondition(Predicates.alwaysTrue());
+        action.initialize();
+    }
+    
+    @Test public void testNoContext() {
+        final Event event = action.execute(src);
+        
+        ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_SUBJECT_C14N_CTX);
+    }
+
+    @Test public void testNoPrincipal() {
+        final Subject subject = new Subject();
+        prc.getSubcontext(SubjectCanonicalizationContext.class, true).setSubject(subject);
+        
+        final Event event = action.execute(src);
+        
+        ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_SUBJECT);
+        Assert.assertNotNull(prc.getSubcontext(SubjectCanonicalizationContext.class, false).getException());
+    }
+
+    @Test public void testMultiPrincipals() {
+        final Subject subject = new Subject();
+        subject.getPrincipals().add(new OIDCSubjectIdentifierPrincipal("foo"));
+        subject.getPrincipals().add(new OIDCSubjectIdentifierPrincipal("bar"));
+        prc.getSubcontext(SubjectCanonicalizationContext.class, true).setSubject(subject);
+        
+        final Event event = action.execute(src);
+        
+        ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_SUBJECT);
+        Assert.assertNotNull(prc.getSubcontext(SubjectCanonicalizationContext.class, false).getException());
+    }
+
+    @Test public void testSuccess() {
+        final Subject subject = new Subject();
+        subject.getPrincipals().add(new OIDCSubjectIdentifierPrincipal("foo"));
+        prc.getSubcontext(SubjectCanonicalizationContext.class, true).setSubject(subject);
+        
+        final Event event = action.execute(src);
+        
+        ActionTestingSupport.assertProceedEvent(event);
+        final SubjectCanonicalizationContext sc = prc.getSubcontext(SubjectCanonicalizationContext.class, false);
+        Assert.assertEquals(sc.getPrincipalName(), "foo");
+    }
+
+    @Test public void testTransform() {
+        final Subject subject = new Subject();
+        subject.getPrincipals().add(new OIDCSubjectIdentifierPrincipal("foo at osu.edu"));
+        prc.getSubcontext(SubjectCanonicalizationContext.class, true).setSubject(subject);
+        
+        final Event event = action.execute(src);
+        
+        ActionTestingSupport.assertProceedEvent(event);
+        final SubjectCanonicalizationContext sc = prc.getSubcontext(SubjectCanonicalizationContext.class, false);
+        Assert.assertEquals(sc.getPrincipalName(), "foo");
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/UserInfoEndpointLookupTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/UserInfoEndpointLookupTest.java
new file mode 100644
index 0000000..3746325
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/UserInfoEndpointLookupTest.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.methods.RequestBuilder;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.protocol.HttpContext;
+import org.mockito.Mockito;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.context.navigate.ParentContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.webflow.execution.Event;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.nimbusds.jose.util.StandardCharset;
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
+
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.UserInfoResponseContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.messaging.PlainUserInfoResponse;
+import net.shibboleth.idp.plugin.authn.oidc.rp.messaging.UserInfoResponse;
+import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+
+/**
+ * Tests for the {@link UserInfoEndpointLookup} action.
+ */
+public class UserInfoEndpointLookupTest extends AbstractOIDCTest {
+    
+    /** The action to test.*/
+    @Nonnull private UserInfoEndpointLookup action;
+    
+    
+    @Override
+    @BeforeMethod
+    public void setup() throws Exception {    
+        
+        super.setup();
+        
+        action = new UserInfoEndpointLookup();
+        action.setAuthenticationContextLookupStrategy(new ParentContextLookup<>(AuthenticationContext.class));
+        action.setProfileContextLookupStrategy(new ChildContextLookup<>(ProfileRequestContext.class).compose(
+                new ChildContextLookup<>(AuthenticationContext.class)
+                .compose(new WebflowRequestContextProfileRequestContextLookup())));        
+    }
+    
+    /** 
+     * Test the token exchange works using a mocked HTTP client and response.
+     * Does not check the signature is valid. Given the amount of mocking, is not
+     * a very thorough test.
+     * 
+     * @throws Exception on error.
+     */
+    @Test
+    public void testLookupSuccess() throws Exception {
+
+        final HttpClient httpClient = Mockito.mock(HttpClient.class);
+        final HttpResponse httpResponse = Mockito.mock(HttpResponse.class);
+        final StatusLine statusLine = Mockito.mock(StatusLine.class);
+
+        Mockito.when(httpResponse.getStatusLine()).thenReturn(statusLine);
+
+        Mockito.when(statusLine.getStatusCode()).thenReturn(200);
+        Mockito.when(httpResponse.getEntity()).thenReturn(new StringEntity(USERINFO_RESPONSE));
+        Mockito.when(httpClient.execute((HttpUriRequest) Mockito.any(), (HttpContext) Mockito.any()))
+                .thenReturn(httpResponse);
+
+        // create new client with mock response
+        action.setHttpClient(httpClient);
+        
+        action.setHttpRequestEncoderStrategy(prc -> {
+            URI uri;
+            try {
+                uri = new URIBuilder().setScheme("https")
+                        .setHost("op.example.com")
+                        .setPath("/userinfo")
+                        .build();
+            } catch (final URISyntaxException e) {
+                return null;
+            }                 
+            
+            // Add headers and create request.
+            final RequestBuilder rb = RequestBuilder.post().setUri(uri)
+                    .setHeader("Content-Type", ContentType.APPLICATION_FORM_URLENCODED.getMimeType())
+                    .setCharset(StandardCharset.UTF_8);
+            return rb.build();
+        });     
+        action.setHttpResponseDecoderStrategy(response -> {
+            final ObjectMapper mapper = new ObjectMapper();
+            try {                
+                final Map<String, Object> responseMap = mapper.readValue(
+                        httpResponse.getEntity().getContent(), new TypeReference<Map<String, Object>>() {});
+                final ClaimsSet claims = new ClaimsSet();
+                claims.putAll(responseMap);
+                return new PlainUserInfoResponse(claims);
+            } catch (final UnsupportedOperationException | IOException e) {                
+                return null;
+            }
+        });
+        
+        action.initialize();
+
+        final Event event = action.execute(src);
+        //Null is success
+        assertNull(event);
+        assertNotNull(prc.getInboundMessageContext().getSubcontext(UserInfoResponseContext.class));
+        assertNotNull(prc.getInboundMessageContext().getSubcontext(UserInfoResponseContext.class)
+                .getUserInfo());
+        assertNotNull(prc.getInboundMessageContext().getSubcontext(UserInfoResponseContext.class)
+                .getUserInfo().getClaimsSet());
+        assertNotNull(prc.getInboundMessageContext().getSubcontext(UserInfoResponseContext.class)
+                .getUserInfo().getClaimsSet().getClaim("sub"),"248289761001");
+
+    }
+    
+    @Test
+    public void testNullUserInfoResponse() throws Exception {
+
+        final HttpClient httpClient = Mockito.mock(HttpClient.class);
+        final HttpResponse httpResponse = Mockito.mock(HttpResponse.class);
+        final StatusLine statusLine = Mockito.mock(StatusLine.class);
+        Mockito.when(httpResponse.getStatusLine()).thenReturn(statusLine);
+        Mockito.when(statusLine.getStatusCode()).thenReturn(200);  
+        
+        // create new client with mock response
+        action.setHttpClient(httpClient);
+        
+        action.setHttpRequestEncoderStrategy(prc -> {
+            URI uri;
+            try {
+                uri = new URIBuilder().setScheme("https")
+                        .setHost("op.example.com")
+                        .setPath("/userinfo")
+                        .build();
+            } catch (final URISyntaxException e) {
+                return null;
+            }                 
+            
+            // Add headers and create request.
+            final RequestBuilder rb = RequestBuilder.post().setUri(uri)
+                    .setHeader("Content-Type", ContentType.APPLICATION_FORM_URLENCODED.getMimeType())
+                    .setCharset(StandardCharset.UTF_8);
+            return rb.build();
+        });     
+        action.setHttpResponseDecoderStrategy(response -> null);
+        
+        action.initialize();
+
+        final Event event = action.execute(src);
+
+        //Null is success
+        assertNotNull(event);
+        assertEquals(event.getId(), AuthnEventIds.AUTHN_EXCEPTION);
+
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateResponseStateTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateResponseStateTest.java
new file mode 100644
index 0000000..6cf31ad
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateResponseStateTest.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.impl;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.context.navigate.ParentContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.webflow.execution.Event;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.oauth2.sdk.id.State;
+import com.nimbusds.openid.connect.sdk.AuthenticationResponseParser;
+
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+import net.shibboleth.idp.relyingparty.RelyingPartyConfiguration;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+/** Tests for {@link AddRedirectURI}.*/
+public class ValidateResponseStateTest extends AbstractOIDCTest {
+    
+    /** The action to test.*/
+    private ValidateResponseState action;
+    
+    /** The RPC.*/
+    private RelyingPartyContext rpc;
+    
+    /** The profile config.*/
+    private OIDCAuthorizationConfiguration oidcAuthzConfig;
+    
+    @Override
+    @BeforeMethod
+    public void setup() throws Exception {
+        super.setup();
+        action = new ValidateResponseState(); 
+        
+        rpc = prc.getSubcontext(RelyingPartyContext.class, true); 
+        oidcAuthzConfig = new OIDCAuthorizationConfiguration();
+        final RelyingPartyConfiguration rpConfig = new RelyingPartyConfiguration();
+        rpc.setProfileConfig(oidcAuthzConfig);
+        rpc.setConfiguration(rpConfig);
+        
+        action.setProfileContextLookupStrategy(new ChildContextLookup<>(ProfileRequestContext.class).compose(
+                new ChildContextLookup<>(AuthenticationContext.class)
+                .compose(new WebflowRequestContextProfileRequestContextLookup())));     
+        
+        action.setAuthenticationContextLookupStrategy(new ParentContextLookup<>(AuthenticationContext.class));
+        
+    }
+    
+    @Test
+    public void testSuccess() throws ComponentInitializationException, URISyntaxException {     
+       
+        action.initialize();        
+        final Event event = action.execute(src);
+        
+        assertNull(event);
+       
+    }
+    
+    @Test
+    public void testNoStateInRequest() throws ComponentInitializationException, URISyntaxException {        
+        
+        authnRequest.setState(null);
+        action.initialize();        
+        final Event event = action.execute(src);
+        
+        assertEquals(event.getId(),  AuthnEventIds.NO_CREDENTIALS);
+    }
+    
+    @Test
+    public void testNoStateInResponse() throws Exception {        
+        
+        prc.getInboundMessageContext().setMessage(AuthenticationResponseParser.parse(
+                new URI("/idp/profile/Authn/OIDC/RP/callback"
+                        + "?state=wrong-state&code=z8C2DCp6sn0D9aGbEqlrFesdPVRXPtDX")));
+        action.initialize();        
+        final Event event = action.execute(src);
+        
+        assertEquals(event.getId(),  AuthnEventIds.NO_CREDENTIALS);
+    }
+    
+    @Test
+    public void testStateMismatch() throws ComponentInitializationException, URISyntaxException {        
+        
+        authnRequest.setState(new State("does-not-match"));
+        action.initialize();        
+        final Event event = action.execute(src);
+        
+        assertEquals(event.getId(),  AuthnEventIds.NO_CREDENTIALS);
+    }
+
+
+}
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCProviderMetadataLookupHandlerTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCProviderMetadataLookupHandlerTest.java
new file mode 100644
index 0000000..f501729
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCProviderMetadataLookupHandlerTest.java
@@ -0,0 +1,229 @@
+/*
+ * 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.metadata.impl;
+
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.fail;
+
+import java.util.List;
+
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.AbstractOIDCEntityContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.impl.AbstractOIDCTest;
+import net.shibboleth.oidc.metadata.ProviderMetadataResolver;
+import net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext;
+import net.shibboleth.oidc.metadata.criterion.IssuerIDCriterion;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+/** Tests for OIDCClientMetadataLookupHandler.*/
+public class OIDCProviderMetadataLookupHandlerTest extends AbstractOIDCTest {
+    
+    /** The handler to test.*/
+    private OIDCProviderMetadataLookupHandler handler;
+    
+    private AbstractOIDCEntityContext ctx;
+    
+    @Override
+    @BeforeMethod
+    public void setup() throws Exception {
+        super.setup();
+        handler = new OIDCProviderMetadataLookupHandler();
+        // Create a resolver that spits out metadata if the issuer criterion matches
+        handler.setProviderMetadataResolver(new ProviderMetadataResolver() {            
+            @Override
+            public String getId() {
+               return "mock-resolver";
+            }            
+            @Override
+            public OIDCProviderMetadata resolveSingle(final CriteriaSet criteria) throws ResolverException {
+                try {
+                    final OIDCProviderMetadata metadata =
+                            OIDCProviderMetadata.parse(GOOD_PROVIDER_CONFIGURATION_INFO);
+                    if (criteria.get(IssuerIDCriterion.class).getIssuerID().equals(metadata.getIssuer())) {
+                        return metadata;
+                    }
+                    return null;
+                } catch (final ParseException e) {
+                    fail(e.getMessage());
+                    return null;
+                }
+            }            
+            @Override
+            public Iterable<OIDCProviderMetadata> resolve(final CriteriaSet criteria) throws ResolverException {
+                return List.of(resolveSingle(criteria));
+            }
+        });
+        ctx = new AbstractOIDCEntityContext();
+        ctx.setIdentifier("https://op.example.com/");
+        handler.setContextClassLookupStrategy(m -> ctx);
+    }
+    
+    @Test
+    public void testLookupSuccess() throws Exception {
+        handler.initialize();
+        
+        handler.invoke(prc.getOutboundMessageContext());
+        assertNotNull(ctx.getSubcontext(OIDCProviderMetadataContext.class));
+        assertNotNull(ctx.getSubcontext(OIDCProviderMetadataContext.class).getProviderInformation());
+        assertEquals(ctx.getSubcontext(OIDCProviderMetadataContext.class)
+                .getProviderInformation().getIssuer().getValue(),"https://op.example.com/");
+    }
+    
+    @Test
+    public void testLookupCopyExisting() throws Exception {
+        
+        handler.setCopyContextStrategy(msg -> {
+            final OIDCProviderMetadataContext ctxt = new OIDCProviderMetadataContext();
+            try {
+                ctxt.setProviderInformation(OIDCProviderMetadata.parse(GOOD_PROVIDER_CONFIGURATION_INFO));
+            } catch (final ParseException e) {
+                return null;
+            }
+            return ctxt;
+        });
+        
+        handler.initialize();
+        
+        handler.invoke(prc.getOutboundMessageContext());
+        assertNotNull(ctx.getSubcontext(OIDCProviderMetadataContext.class));
+        assertNotNull(ctx.getSubcontext(OIDCProviderMetadataContext.class).getProviderInformation());
+        assertEquals(ctx.getSubcontext(OIDCProviderMetadataContext.class)
+                .getProviderInformation().getIssuer().getValue(),"https://op.example.com/");
+    }
+    
+    @Test
+    public void testLookupCopyExisting_NoProviderInformation() throws Exception {
+        
+        // Prevent resolver from returning metadata if copy does not work
+        handler.setProviderMetadataResolver(new ProviderMetadataResolver() {            
+            @Override
+            public String getId() {
+               return "mock-resolver";
+            }            
+            @Override
+            public OIDCProviderMetadata resolveSingle(final CriteriaSet criteria) throws ResolverException {
+               return null;
+            }            
+            @Override
+            public Iterable<OIDCProviderMetadata> resolve(final CriteriaSet criteria) throws ResolverException {
+                return List.of(resolveSingle(criteria));
+            }
+        });
+        
+        // Do not set provider information
+        handler.setCopyContextStrategy(msg -> new OIDCProviderMetadataContext());
+        
+        handler.initialize();
+        
+        handler.invoke(prc.getOutboundMessageContext());
+        assertNull(ctx.getSubcontext(OIDCProviderMetadataContext.class));
+
+    }
+    
+    @Test
+    public void testLookupCopyExisting_WrongIssuer() throws Exception {
+        
+        ctx.setIdentifier("https://wrong-issuer.example.com/");
+        handler.setCopyContextStrategy(msg -> {
+            final OIDCProviderMetadataContext ctxt = new OIDCProviderMetadataContext();
+            try {
+                ctxt.setProviderInformation(OIDCProviderMetadata.parse(GOOD_PROVIDER_CONFIGURATION_INFO));
+            } catch (final ParseException e) {
+                return null;
+            }
+            return ctxt;
+        });
+        
+        handler.initialize();
+        
+        handler.invoke(prc.getOutboundMessageContext());
+        assertNull(ctx.getSubcontext(OIDCProviderMetadataContext.class));
+
+    }
+    
+    @Test
+    public void testLookupNoMetadata() throws Exception {
+        
+        handler.setProviderMetadataResolver(new ProviderMetadataResolver() {            
+            @Override
+            public String getId() {
+               return "mock-resolver";
+            }            
+            @Override
+            public OIDCProviderMetadata resolveSingle(final CriteriaSet criteria) throws ResolverException {
+               return null;
+            }            
+            @Override
+            public Iterable<OIDCProviderMetadata> resolve(final CriteriaSet criteria) throws ResolverException {
+                return List.of(resolveSingle(criteria));
+            }
+        });
+        
+        handler.initialize();
+        
+        handler.invoke(prc.getOutboundMessageContext());
+        assertNull(ctx.getSubcontext(OIDCProviderMetadataContext.class));
+
+    }
+    
+    @Test
+    public void testLookupWrongIssuer() throws Exception {
+        
+        ctx.setIdentifier("https://wrong-issuer.example.com/");
+        
+        handler.initialize();
+        
+        handler.invoke(prc.getOutboundMessageContext());
+        assertNull(ctx.getSubcontext(OIDCProviderMetadataContext.class));
+    }
+    
+    @Test(expectedExceptions = MessageHandlerException.class)
+    public void testLookupResolverException() throws Exception {
+        
+        handler.setProviderMetadataResolver(new ProviderMetadataResolver() {            
+            @Override
+            public String getId() {
+               return "mock-resolver";
+            }            
+            @Override
+            public OIDCProviderMetadata resolveSingle(final CriteriaSet criteria) throws ResolverException {
+               throw new ResolverException("Resolver exception");
+            }            
+            @Override
+            public Iterable<OIDCProviderMetadata> resolve(final CriteriaSet criteria) throws ResolverException {
+                throw new ResolverException("Resolver exception");
+            }
+        });
+        
+        handler.initialize();
+        
+        handler.invoke(prc.getOutboundMessageContext());
+
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/test/resources/conf/authn/oidc-rp-credentials.xml b/idp-oidc-rp-impl/src/test/resources/conf/authn/oidc-rp-credentials.xml
index b3e52af..4a925b6 100644
--- a/idp-oidc-rp-impl/src/test/resources/conf/authn/oidc-rp-credentials.xml
+++ b/idp-oidc-rp-impl/src/test/resources/conf/authn/oidc-rp-credentials.xml
@@ -24,16 +24,6 @@
         p:resource="%{idp.authn.oidc.rp.client.enc.key:#{null}}" />
     </util:list>
     
-    <util:list id="shibboleth.authn.oidc.rp.DefaultRequestObjectKeyTransportEncryptionCredentials">
-        <bean parent="shibboleth.authn.oidc.rp.JWKCredential" p:failIfResourceIsNull="false" 
-        p:resource="%{idp.authn.oidc.rp.client.requestobject.enc.symmetric.kw.key:#{null}}" />
-    </util:list>
-    
-    <util:list id="shibboleth.authn.oidc.rp.DefaultRequestObjectDataEncryptionCredentials">
-        <bean parent="shibboleth.authn.oidc.rp.JWKCredential" p:failIfResourceIsNull="false" 
-        p:resource="%{idp.authn.oidc.rp.client.requestobject.enc.symmetric.cek.key:#{null}}" />
-    </util:list>
-    
      <!-- Default signing credentials -->
      <util:list id="shibboleth.authn.oidc.rp.DefaultSigningCredentials">
         <bean parent="shibboleth.authn.oidc.rp.JWKCredential" p:failIfResourceIsNull="false" 

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


More information about the commits mailing list