[java-idp-plugin-oidc-rp] branch main updated: JOIDCRP-14 and JOIDCRP-13 - Client Authentication/Identifier Resolver

Phil Smart philip.smart at jisc.ac.uk
Thu Mar 24 10:36:18 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=1dd27e4f890344f72c1ddd0409c6ace3c7a5fc7c

The following commit(s) were added to refs/heads/main by this push:
     new 1dd27e4  JOIDCRP-14 and JOIDCRP-13 - Client Authentication/Identifier Resolver
1dd27e4 is described below

commit 1dd27e4f890344f72c1ddd0409c6ace3c7a5fc7c
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Mar 24 10:36:12 2022 +0000

    JOIDCRP-14 and JOIDCRP-13 - Client Authentication/Identifier Resolver
    
     - Remove the client authentication/identifier resolvers in favour of
    strategies applied to the profile configuration.
     - By default, these strategies take values from the oidc properties
    file for quick configuration of a single client.
     - If values are not supplied in the properties file, a further strategy
    looks them up from a storage service which allows multiple clients to be
    defined.
     - The default storage services are based on maps which need to be
    defined in the relying-party.xml file - and are then auto-reloaded etc.
    by the relyingparty resolver.
    
    
    https://shibboleth.atlassian.net/browse/JOIDCRP-13
    https://shibboleth.atlassian.net/browse/JOIDCRP-14
---
 .../config/navigate/RedirectUriLookupFunction.java |   3 +-
 idp-oidc-rp-impl/pom.xml                           |   5 +
 ...AbstractClientAuthenticationLookupStrategy.java |  74 ++++++
 .../DefaultClientAuthenticationLookupStrategy.java | 111 ++++++++
 .../DefaultClientIdentifierLookupStrategy.java     |  77 ++++++
 .../MapBackedMemoryStorageServiceFactoryBean.java  |  23 +-
 ...ceBackedClientAuthenticationLookupStrategy.java | 291 +++++++++++++++++++++
 ...erviceBackedClientIdentifierLookupStrategy.java | 122 +++++++++
 .../plugin/authn/oidc/rp/config/package-info.java  |  21 ++
 .../impl/AbstractHttpOIDCAuthenticationAction.java |   5 +-
 .../oidc/rp/impl/AddOIDCAuthenticationRequest.java |  17 +-
 .../oidc/rp/impl/ExchangeCodeForAccessToken.java   |   2 +-
 ...nitializeOAuth2ClientAuthenticationContext.java | 123 ++++-----
 .../rp/impl/InitializeOAuth2ClientContext.java     |  96 ++++---
 .../impl/OAuth2ClientAuthenticationContainer.java  | 147 -----------
 ...lientAuthenticationResolverServiceStrategy.java |  65 -----
 .../impl/OAuth2ClientIdentifierContainer.java      | 148 -----------
 ...th2ClientIdentifierResolverServiceStrategy.java |  67 -----
 ...eloadingOAuth2ClientAuthenticationProvider.java | 107 --------
 .../ReloadingOAuth2ClientIdentifierProvider.java   | 105 --------
 .../META-INF/net.shibboleth.idp/postconfig.xml     | 247 +++++------------
 .../oidc-relying-party-authn-beans.xml             |   2 -
 .../oidc-relying-party-authn-flow.xml              |   2 +-
 .../idp/service/relying-party/postconfig.xml       |  93 +++++++
 .../authn/clientauthentication-resolver-system.xml |  33 ---
 .../authn/clientidentifier-resolver-system.xml     |  30 ---
 .../authn/oidc/rp/conf/authn/oidc-rp.properties    |  26 +-
 ...aultClientAuthenticationLookupStrategyTest.java |  97 +++++++
 ...ckedClientAuthenticationLookupStrategyTest.java | 202 ++++++++++++++
 .../plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java  |  63 +++--
 .../resources/conf/test-relying-party-system.xml   |  16 ++
 .../conf/test-relyingparty-resolver-service.xml    |  22 +-
 32 files changed, 1380 insertions(+), 1062 deletions(-)

diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/RedirectUriLookupFunction.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/RedirectUriLookupFunction.java
index fdeece6..6cc382f 100644
--- a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/RedirectUriLookupFunction.java
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/RedirectUriLookupFunction.java
@@ -41,7 +41,8 @@ public class RedirectUriLookupFunction extends AbstractRelyingPartyLookupFunctio
             final ProfileConfiguration pc = rpc.getProfileConfig();
             if (pc instanceof OIDCAuthorizationConfiguration) {
                 try {
-                    return new URI(((OIDCAuthorizationConfiguration) pc).getRedirectUriOverride(input));
+                    final String uriString = ((OIDCAuthorizationConfiguration) pc).getRedirectUriOverride(input);
+                    return uriString != null ? new URI(uriString): null;
                 } catch (final URISyntaxException e) {
                     return null;
                 }
diff --git a/idp-oidc-rp-impl/pom.xml b/idp-oidc-rp-impl/pom.xml
index 69a5e33..6ad016c 100644
--- a/idp-oidc-rp-impl/pom.xml
+++ b/idp-oidc-rp-impl/pom.xml
@@ -111,6 +111,11 @@
             <artifactId>idp-admin-impl</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
         <!-- Test dependency -->
         <dependency>
             <groupId>net.shibboleth.idp.plugin.authn.test</groupId>
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/AbstractClientAuthenticationLookupStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/AbstractClientAuthenticationLookupStrategy.java
new file mode 100644
index 0000000..63330fc
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/AbstractClientAuthenticationLookupStrategy.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.config;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
+import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
+import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
+import com.nimbusds.oauth2.sdk.auth.ClientSecretPost;
+import com.nimbusds.oauth2.sdk.auth.Secret;
+import com.nimbusds.oauth2.sdk.id.ClientID;
+
+import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
+
+/** Base function to support lookup of a suitable client authentication methods.*/
+public abstract class AbstractClientAuthenticationLookupStrategy extends AbstractIdentifiableInitializableComponent
+            implements Function<ProfileRequestContext, ClientAuthentication> {
+    
+    /** Class logger. */
+    private final Logger log = LoggerFactory.getLogger(AbstractClientAuthenticationLookupStrategy.class);
+    
+    /**
+     * Construct the client authentication from the given client authentication record.
+     * 
+     * @param clientId the client_id
+     * @param tokenEndpointAuthMethod the token endpoint authentication method
+     * @param clientSecret the client secret
+     * 
+     * @return the constructed client authentication
+     */
+    @Nullable protected ClientAuthentication constructClientAuthentication(
+            @Nonnull final String clientId, @Nonnull final String tokenEndpointAuthMethod,
+            @Nonnull final Secret clientSecret) {
+
+        if (clientSecret.expired()) {
+            log.warn("{} Client secret has expired for client '{}'", getId(), clientId);
+            return null;
+        }
+        final ClientAuthenticationMethod method = new ClientAuthenticationMethod(tokenEndpointAuthMethod);
+        if (method.equals(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)) {
+            return new ClientSecretBasic(new ClientID(clientId), clientSecret);
+        } else if (method.equals(ClientAuthenticationMethod.CLIENT_SECRET_POST)) {
+            return new ClientSecretPost(new ClientID(clientId), clientSecret);
+        }  
+        log.warn("{}: Client authentication method '{}' not supported for client '{}'", getId(), 
+                tokenEndpointAuthMethod, clientId);
+        return null;
+        
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientAuthenticationLookupStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientAuthenticationLookupStrategy.java
new file mode 100644
index 0000000..189b5f7
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientAuthenticationLookupStrategy.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.config;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Date;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
+import com.nimbusds.oauth2.sdk.auth.Secret;
+
+import net.shibboleth.utilities.java.support.annotation.ParameterName;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * A strategy that produces a client authentication method either directly from the information supplied, 
+ * or derived from a lookup strategy. 
+ */
+ at ThreadSafe
+public class DefaultClientAuthenticationLookupStrategy extends AbstractClientAuthenticationLookupStrategy {
+    
+    /** A fixed client_id to use.*/
+    @Nullable private final String clientId;
+    
+    /** A fixed client_secret to use over any injected strategy to locate one .*/
+    @Nullable private final String clientSecret;
+    
+    /** A fixed client authentication method to use.*/
+    @Nullable private final String clientAuthenticationMethod;
+    
+    /** A fixed client secret expiry in seconds since UNIX EPOCH to use.*/
+    @Nullable private final Duration clientSecretExpiresAt;
+    
+    /** The strategy used to locate the client authentication method if a fixed one is not already supplied.*/
+    @Nullable private final Function<ProfileRequestContext, ClientAuthentication> clientAuthenticationLookupStrategy;
+    
+
+    /**
+     * 
+     * Constructor.
+     *
+     * @param id the client_id
+     * @param secret the client_secret
+     * @param authenticationMethod the client authentication method
+     * @param secretExpiresAt when the client secret expires, or 0 for no expiry
+     * @param strategy the strategy to lookup the client authentication 
+     * @throws ComponentInitializationException
+     */
+    public DefaultClientAuthenticationLookupStrategy(
+            @ParameterName(name="clientId") @Nullable final String id,
+            @ParameterName(name="clientSecret") @Nullable final String secret,
+            @ParameterName(name="clientAuthenticationMethod") @Nullable final String authenticationMethod,
+            @ParameterName(name="clientSecretExpiresAt") @Nullable final Duration secretExpiresAt,
+            @ParameterName(name="clientIdLookupStrategy") 
+                @Nullable final Function<ProfileRequestContext, ClientAuthentication> strategy) 
+                    throws ComponentInitializationException {
+        
+     
+        if (strategy == null && secret == null && authenticationMethod == null && secretExpiresAt == null) {
+            
+            throw new ComponentInitializationException("Must supply a client_secret, client_authentication_method,"
+                    + " and client_secret_expires_at if a client authentication lookup strategy is not supplied");
+        }
+        clientId = StringSupport.trimOrNull(id);
+        clientSecret = StringSupport.trimOrNull(secret);
+        clientAuthenticationMethod = StringSupport.trimOrNull(authenticationMethod);
+        clientSecretExpiresAt = secretExpiresAt;
+        clientAuthenticationLookupStrategy = strategy;
+    }
+
+    @Override
+    @Nullable public ClientAuthentication apply(@Nonnull final ProfileRequestContext profileRequestContext) {
+        
+        // Use supplied client authentication parameters first
+        if (clientSecret != null && clientAuthenticationMethod != null && clientSecretExpiresAt != null
+                 && clientId != null) {
+            if (clientSecretExpiresAt.toSeconds() == 0) {
+                // No expiry if 0, so do not set. 
+                return constructClientAuthentication(clientId, clientAuthenticationMethod, new Secret(clientSecret));
+                
+            }            
+            return constructClientAuthentication(clientId, clientAuthenticationMethod, new Secret(clientSecret, 
+                    Date.from(Instant.ofEpochSecond(clientSecretExpiresAt.toSeconds()))));
+        }
+        return clientAuthenticationLookupStrategy.apply(profileRequestContext);
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientIdentifierLookupStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientIdentifierLookupStrategy.java
new file mode 100644
index 0000000..784e674
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientIdentifierLookupStrategy.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.config;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.utilities.java.support.annotation.ParameterName;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * A strategy that produces a client_id either directly from the one supplied, or derived from the 
+ * lookup strategy. 
+ */
+ at ThreadSafe
+public class DefaultClientIdentifierLookupStrategy implements Function<ProfileRequestContext, String> {
+    
+    /** A fixed client_id to use over any injected strategy to locate one.*/
+    @Nullable private final String clientId;
+    
+    /** The strategy used to locate the client_id if a fixed one is not already supplied.*/
+    @Nullable private final Function<ProfileRequestContext, String> clientIdLookupStrategy;
+    
+    /**
+     * 
+     * Constructor.
+     *
+     * @param id a client_id that takes precedence over one derived from the lookup strategy.
+     * @param strategy the strategy used to derive a client_id for the input profile request context.
+     * 
+     * @throws ComponentInitializationException if both id and strategy are null, one is required.
+     */
+    public DefaultClientIdentifierLookupStrategy(@ParameterName(name="clientId") @Nullable final String id, 
+            @ParameterName(name="clientIdLookupStrategy") 
+                @Nullable final Function<ProfileRequestContext, String> strategy) 
+                    throws ComponentInitializationException {
+     
+        if (id == null && strategy == null) {
+            throw new ComponentInitializationException("Must supply either a fixed client_id value or a "
+                    + "client_id lookup strategy");
+        }
+        clientId = StringSupport.trimOrNull(id);
+        clientIdLookupStrategy = strategy;
+    }
+
+    @Override
+    @Nullable public String apply(@Nonnull final ProfileRequestContext profileRequestContext) {
+        
+        // Use supplied client_id first
+        if (clientId != null) {
+            return clientId;
+        }
+        return clientIdLookupStrategy.apply(profileRequestContext);
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/MapBackedMemoryStorageServiceFactoryBean.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/MapBackedMemoryStorageServiceFactoryBean.java
similarity index 84%
rename from idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/MapBackedMemoryStorageServiceFactoryBean.java
rename to idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/MapBackedMemoryStorageServiceFactoryBean.java
index 10f5328..469c9d3 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/MapBackedMemoryStorageServiceFactoryBean.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/MapBackedMemoryStorageServiceFactoryBean.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+package net.shibboleth.idp.plugin.authn.oidc.rp.config;
 
 import java.util.Collections;
 import java.util.Map;
@@ -23,8 +23,12 @@ import java.util.Map;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.GuardedBy;
+import javax.annotation.concurrent.ThreadSafe;
 
+import org.opensaml.storage.StorageService;
 import org.opensaml.storage.impl.MemoryStorageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.config.AbstractFactoryBean;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -33,13 +37,16 @@ import com.fasterxml.jackson.databind.ObjectWriter;
 import net.shibboleth.utilities.java.support.annotation.ParameterName;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
-
 /** 
- * A factory that creates and populates a map backed MemoryStorageService from an injected map.
+ * A factory that creates and populates a map backed {@link MemoryStorageService} from an injected map.
  * Values which are *not* Strings are serialized into JSON using the supplied object mapper before they are added
  * to the storage service. Values which are already Strings are just passed in without modification.
  */
-public class MapBackedMemoryStorageServiceFactoryBean extends AbstractFactoryBean<MemoryStorageService> {
+ at ThreadSafe
+public class MapBackedMemoryStorageServiceFactoryBean extends AbstractFactoryBean<StorageService> {
+    
+    /** Class logger. */
+    private final Logger log = LoggerFactory.getLogger(MapBackedMemoryStorageServiceFactoryBean.class);
     
     /** The Map to inject values into the storage service with.*/
     @Nonnull private final Map<String, Object> mapToInject;
@@ -47,7 +54,7 @@ public class MapBackedMemoryStorageServiceFactoryBean extends AbstractFactoryBea
     /** The ID to give the created memory service.*/
     @Nonnull private final String identifier;
     
-    /** The partition name to use in the injected storage service.*/
+    /** The partition name to use in the storage service.*/
     @Nonnull private final String storageServiceContext;
     
     /** JSON object mapper. */
@@ -94,11 +101,11 @@ public class MapBackedMemoryStorageServiceFactoryBean extends AbstractFactoryBea
 
     @Override
     public Class<?> getObjectType() {
-        return MemoryStorageService.class;
+        return StorageService.class;
     }
 
     @Override
-    protected MemoryStorageService createInstance() throws Exception {
+    protected StorageService createInstance() throws Exception {
         final MemoryStorageService service = new MemoryStorageService();
         service.setId(identifier);
         service.initialize();
@@ -113,6 +120,8 @@ public class MapBackedMemoryStorageServiceFactoryBean extends AbstractFactoryBea
             }
             service.create(storageServiceContext, entry.getKey(), objectAsString, null);
         }
+        log.debug("Created a map based memory storage service '{}' with '{}' records", 
+                identifier, mapToInject.size());
         return service;
     }
 
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientAuthenticationLookupStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientAuthenticationLookupStrategy.java
new file mode 100644
index 0000000..eaa81d2
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientAuthenticationLookupStrategy.java
@@ -0,0 +1,291 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.config;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.time.Instant;
+import java.util.Date;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import javax.json.Json;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+import javax.json.JsonStructure;
+import javax.json.stream.JsonGenerator;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
+import org.opensaml.storage.StorageRecord;
+import org.opensaml.storage.StorageSerializer;
+import org.opensaml.storage.StorageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
+import com.nimbusds.oauth2.sdk.auth.Secret;
+
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+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.primitive.StringSupport;
+
+/**
+ * Default strategy for locating a configured client authentication method appropriate for the client_id
+ * stashed in the {@link OAuth2ClientContext}.
+ */
+public class StorageServiceBackedClientAuthenticationLookupStrategy 
+                            extends AbstractClientAuthenticationLookupStrategy  {
+    
+    /** The context name in the {@link StorageService}. */
+    @Nonnull @NotEmpty public static final String CONTEXT_NAME = "oauth2ClientAuthentication";
+    
+    /** Class logger. */
+    private final Logger log = LoggerFactory.getLogger(StorageServiceBackedClientAuthenticationLookupStrategy.class);
+    
+    /** The strategy used to lookup or create the {@link OAuth2ClientContext}.*/
+    @NonnullAfterInit 
+    private Function<ProfileRequestContext, OAuth2ClientContext> oauth2ClientContextLookupStrategy;
+    
+    /** The {@link StorageService} used to store a client identifier for a given Issuer. */
+    @NonnullAfterInit private StorageService storageService;
+    
+    /** 
+     * A storage service serializer for serializing and deserializing ClientAuthenticationRecords out of 
+     * storage records.
+     */
+    @Nonnull private StorageSerializer<ClientAuthenticationRecord> storageSerializer;
+    
+    /** Constructor. */
+    public StorageServiceBackedClientAuthenticationLookupStrategy() {
+        oauth2ClientContextLookupStrategy = new ChildContextLookup<>(OAuth2ClientContext.class).compose(
+                new ChildContextLookup<>(OIDCPeerEntityContext.class).compose(
+                        new OutboundMessageContextLookup()));
+        
+        storageSerializer = new ClientAuthenticationStorageRecordSerializer();
+    }
+    
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (oauth2ClientContextLookupStrategy == null) {
+            throw new ComponentInitializationException("OAuth2ClientContextLookupStrategy cannot be null");
+        }
+        if (storageService == null) {
+            throw new ComponentInitializationException("StorageService cannot be null");
+        }
+    }
+    
+    /**
+     * Set the storage serializer to use with the storage service. 
+     * 
+     * @param serializer the storage serializer
+     */
+    public void setStorageSerializer(final StorageSerializer<ClientAuthenticationRecord> serializer) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+        
+        storageSerializer = Constraint.isNotNull(serializer, "Storage serializer can not be null");
+    }
+    
+    /**
+     * Set the strategy to lookup the {@link OAuth2ClientContext} 
+     * from the {@link ProfileRequestContext}.
+     * 
+     * @param strgy the strategy.
+     */
+    public void setOAuth2ClientContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, OAuth2ClientContext> strgy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+        oauth2ClientContextLookupStrategy = Constraint.isNotNull(strgy, 
+                "OAuth2 client context lookup strategy cannot be null");
+    }
+ 
+    /**
+     * Set the {@link StorageService} back-end to use.
+     * 
+     * @param storage the back-end to use
+     */
+    public void setStorageService(@Nonnull final StorageService storage) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+        storageService = Constraint.isNotNull(storage, "StorageService cannot be null");
+    }
+
+    @Override
+    @Nullable public ClientAuthentication apply(@Nonnull final ProfileRequestContext profileRequestContext) {
+        final OAuth2ClientContext clientCtx = oauth2ClientContextLookupStrategy.apply(profileRequestContext);
+        if (clientCtx == null || StringSupport.trimOrNull(clientCtx.getClientId()) == null) {
+            return null;
+        }
+        try {
+            final StorageRecord<ClientAuthenticationRecord> record = 
+                    storageService.read(CONTEXT_NAME, clientCtx.getClientId());
+            if (record != null) {  
+                final ClientAuthenticationRecord clientAuthRecord = 
+                        record.getValue(storageSerializer, CONTEXT_NAME, clientCtx.getClientId());
+                return constructClientAuthentication(clientCtx.getClientId(),
+                        clientAuthRecord.getTokenEndpointAuthMethod(),
+                        clientAuthRecord.getClientSecret());
+            }
+        } catch (final IOException e) {
+            log.debug("{}: Error retrieving client_authentication record from the storage service", getId(), e);
+            return null;
+        }
+        return null;
+    }
+    
+    
+    
+    /** A Storage service serializer for converting between ClientAuthenticationRecords and JSON.*/
+    private static class ClientAuthenticationStorageRecordSerializer 
+                                    implements StorageSerializer<ClientAuthenticationRecord> {
+
+        /** Class logger. */
+        private final Logger log = LoggerFactory.getLogger(ClientAuthenticationStorageRecordSerializer.class);
+
+        @Override
+        public boolean isInitialized() {
+            // Is initialized once created
+            return true;
+        }
+
+        @Override
+        public void initialize() throws ComponentInitializationException {
+            //no-op here, does not need to be called.            
+        }
+
+        @Override
+        public String serialize(final ClientAuthenticationRecord instance) throws IOException {
+            
+            final StringWriter sink = new StringWriter(128);
+            
+            try (final JsonGenerator gen = Json.createGenerator(sink)) {  
+                
+                final String secretExpiryTimeSinceEpochUTC = Long.toString( 
+                        instance.getClientSecret().getExpirationDate().toInstant().getEpochSecond());
+                gen.writeStartObject()
+                    .write("token_endpoint_auth_method", instance.getTokenEndpointAuthMethod())
+                    .write("client_secret", instance.getClientSecret().getValue())
+                    .write("client_secret_expires_at", secretExpiryTimeSinceEpochUTC);
+                                
+                gen.writeEnd().close();
+                
+                return sink.toString();
+            } catch (final JsonException e) {
+                log.error("Exception while serializing ClientAuthenticationRecord: {}", e.getMessage());
+                throw new IOException("Exception while serializing ClientAuthenticationRecord", e);
+            }
+        }
+
+        @Override
+        public ClientAuthenticationRecord deserialize(
+                final long version, final String context, final String key, final String value,
+                final Long expiration) throws IOException {
+            
+            try (final JsonReader reader = Json.createReader(new StringReader(value))) {
+                
+                final JsonStructure st = reader.read();
+                if (!(st instanceof JsonObject)) {
+                    throw new IOException("Found invalid data structure while parsing ClientAuthenticationRecord");
+                }
+                final JsonObject obj = (JsonObject) st;
+                
+                final String tokenEndpointAuthMethod = obj.getString("token_endpoint_auth_method");
+                final String secret = obj.getString("client_secret");
+                final long expiryInSeconds = obj.getJsonNumber("client_secret_expires_at").longValueExact();
+                
+                if (expiryInSeconds == 0) {
+                    // Then no expiry
+                    return new ClientAuthenticationRecord(tokenEndpointAuthMethod, new Secret(secret));
+                } else {
+                    final Instant secretExpiration = Instant.ofEpochSecond(expiryInSeconds);
+                    return new ClientAuthenticationRecord(
+                            tokenEndpointAuthMethod, new Secret(secret, Date.from(secretExpiration)));
+                }
+                
+
+                
+                
+            } catch (final NullPointerException | ClassCastException | ArithmeticException | JsonException e) {
+                log.error("Exception while parsing ClientAuthenticationRecord: {}", e.getMessage());
+                throw new IOException("Found invalid data structure while parsing ClientAuthenticationRecord", e);
+            }
+        }
+        
+    }
+    
+    /**
+     * A client authentication record that is serialized in and out of a storage service.
+     */
+    @Immutable
+    private static class ClientAuthenticationRecord {
+        
+        /** The client authentication method for the token endpoint.*/
+        @Nonnull private final String tokenEndpointAuthMethod;
+        
+        /** the client secret.*/
+        @Nonnull private final Secret clientSecret;
+
+        /**
+         * Constructor.
+         *
+         * @param authMethod the client authentication method
+         * @param secret the client secret
+         */
+        public ClientAuthenticationRecord(@Nonnull final String authMethod, @Nonnull final Secret secret) {
+            tokenEndpointAuthMethod = 
+                    Constraint.isNotEmpty(authMethod, "Authentication method type can not be null or empty");
+            clientSecret = Constraint.isNotNull(secret, "Client Secrete can not be null");
+        }
+
+        /**
+         * Get the token_endpoint_auth_method.
+         * 
+         * @return Returns the tokenEndpointAuthMethod.
+         */
+        public final String getTokenEndpointAuthMethod() {
+            return tokenEndpointAuthMethod;
+        }
+
+        /**
+         * Get the client_sercret. 
+         * 
+         * @return Returns the clientSecret.
+         */
+        public final Secret getClientSecret() {
+            return clientSecret;
+        }
+        
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientIdentifierLookupStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientIdentifierLookupStrategy.java
new file mode 100644
index 0000000..8270037
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientIdentifierLookupStrategy.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.config;
+
+import java.io.IOException;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.storage.StorageRecord;
+import org.opensaml.storage.StorageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Default strategy for locating a client_id in the storage service based on the issuer_id found 
+ * in the provider metadata.
+ */
+public class StorageServiceBackedClientIdentifierLookupStrategy extends AbstractIdentifiableInitializableComponent
+                            implements Function<ProfileRequestContext, String> {
+    
+    /** The context name in the {@link StorageService}. */
+    @Nonnull @NotEmpty public static final String CONTEXT_NAME = "oauth2ClientIdentifier";
+    
+    /** Class logger. */
+    private final Logger log = LoggerFactory.getLogger(StorageServiceBackedClientIdentifierLookupStrategy.class);
+    
+    /** Lookup strategy to locate the provider metadata to use.*/
+    @NonnullAfterInit 
+    private Function<ProfileRequestContext, OIDCProviderMetadataContext> providerMetadataLookupStrategy;
+    
+    /** The {@link StorageService} used to store a client_id for a given issuer_id. */
+    @NonnullAfterInit private StorageService storageService;
+    
+    
+    /**
+     * Set the lookup strategy to locate the OpenID providers metadata.
+     * 
+     * @param strategy the strategy.
+     */
+    public void setProviderMetadataLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, OIDCProviderMetadataContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        providerMetadataLookupStrategy = 
+                Constraint.isNotNull(strategy,"Provider metadata lookup strategy can not be null");
+    }
+    
+    @Override
+    public void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        if (storageService == null) {
+            throw new ComponentInitializationException(getId() + ": StorageService cannot be null");
+        }
+        if (providerMetadataLookupStrategy == null) {
+            throw new ComponentInitializationException(
+                    getId() + ": OIDC Provider metadata lookup strategy cannot be null");
+        }
+    }   
+ 
+    /**
+     * Set the {@link StorageService} back-end to use.
+     * 
+     * @param storage the back-end to use
+     */
+    public void setStorageService(@Nonnull final StorageService storage) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+        storageService = Constraint.isNotNull(storage, "StorageService cannot be null");
+    }
+
+    @Override
+    @Nullable public String apply(@Nonnull final ProfileRequestContext profileRequestContext) {
+        
+        final OIDCProviderMetadataContext providerMetadataContext = 
+                providerMetadataLookupStrategy.apply(profileRequestContext);
+        if (providerMetadataContext == null || providerMetadataContext.getProviderInformation() == null) {
+            log.warn("{} No provider metadata found for peer,  nothing to do", getId());
+            return null;
+        }
+        
+        try {
+            final StorageRecord<?> record = 
+                    storageService.read(CONTEXT_NAME, 
+                            providerMetadataContext.getProviderInformation().getIssuer().getValue());
+            if (record != null) {
+                return record.getValue();
+            }
+        } catch (final IOException e) {
+            log.warn(getId() + "{}: StorageService read failed, could not acquire client_id for issuer {}", getId(),
+                    providerMetadataContext.getProviderInformation().getIssuer(), e);
+        }
+        return null;
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/package-info.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/package-info.java
new file mode 100644
index 0000000..a1e4a77
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Configuration implementation classes.
+ */
+package net.shibboleth.idp.plugin.authn.oidc.rp.config;
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractHttpOIDCAuthenticationAction.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractHttpOIDCAuthenticationAction.java
index 742bfc6..f3007eb 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractHttpOIDCAuthenticationAction.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractHttpOIDCAuthenticationAction.java
@@ -18,7 +18,6 @@
 package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
 
 import java.io.IOException;
-import java.util.Map;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
@@ -41,7 +40,7 @@ import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
 /**
- * An abstract class for actions that want to make synchronous HTTP requests.
+ * An abstract class for OIDC actions that want to make synchronous HTTP requests.
  * 
  * @param <T> the response type of the object returned as a result of the request.
  */
@@ -166,7 +165,7 @@ public class AbstractHttpOIDCAuthenticationAction<T> extends AbstractOIDCAuthent
             final T responseObject = getHttpResponseDecoderStrategy().apply(response);
             if (responseObject == null) {
                 throw new OIDCRPException(
-                        "Unable to decode HTTP response");                
+                        "Unable to process HTTP response, likely error response");                
             }
             return responseObject;
         } catch (final IOException e) {
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddOIDCAuthenticationRequest.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddOIDCAuthenticationRequest.java
index 9ed4ba5..96e3ea5 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddOIDCAuthenticationRequest.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddOIDCAuthenticationRequest.java
@@ -82,7 +82,7 @@ public class AddOIDCAuthenticationRequest extends AbstractAuthenticationAction {
     
     /** The strategy used to lookup or create the {@link OAuth2ClientContext} for storing the client_id.*/
     @Nonnull 
-    private final Function<ProfileRequestContext, OAuth2ClientContext> oauth2ClientContextLookupStrategy;
+    private Function<ProfileRequestContext, OAuth2ClientContext> oauth2ClientContextLookupStrategy;
     
     /** The stashed OAuth2 client context.*/
     @Nullable private OAuth2ClientContext oauth2ClientContext;
@@ -124,6 +124,21 @@ public class AddOIDCAuthenticationRequest extends AbstractAuthenticationAction {
                 Constraint.isNotNull(strategy,"Provider metadata lookup strategy can not be null");
     }
     
+    /**
+     * Set the strategy to lookup the {@link OAuth2ClientContext} 
+     * from the {@link ProfileRequestContext}.
+     * 
+     * @param strgy the strategy.
+     */
+    public void setOAuth2ClientContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, OAuth2ClientContext> strgy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+        oauth2ClientContextLookupStrategy = Constraint.isNotNull(strgy, 
+                "OAuth2 client context lookup strategy cannot be null");
+    }
+    
     
     /**
      * Set the lookup strategy to locate the response type and mode context.
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ExchangeCodeForAccessToken.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ExchangeCodeForAccessToken.java
index 8d5934d..1879970 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ExchangeCodeForAccessToken.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ExchangeCodeForAccessToken.java
@@ -102,7 +102,7 @@ public class ExchangeCodeForAccessToken extends AbstractHttpOIDCAuthenticationAc
               log.trace("{}: Token request response '{}'",getLogPrefix(), responseObject);
 
           } catch (final OIDCRPException e) {
-              log.error("{} Unable to exchange authorisation code for token result",getLogPrefix(),e);
+              log.error("{} Failed to exchange authorisation code for token result: {}",getLogPrefix(), e.getMessage());
               ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.AUTHN_EXCEPTION);
           }          
     }
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationContext.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationContext.java
index 9ff116a..a2e4867 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationContext.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationContext.java
@@ -31,100 +31,75 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
-import com.nimbusds.oauth2.sdk.id.ClientID;
 
-import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
 import net.shibboleth.idp.profile.AbstractProfileAction;
 import net.shibboleth.idp.profile.IdPEventIds;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.oidc.authn.context.OAuth2ClientAuthenticationContext;
-import net.shibboleth.oidc.metadata.criterion.ClientIDCriterion;
-import net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
 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;
 
 /**
  * An {@link AbstractProfileAction action} that resolves the client authentication method for the chosen 
  * downstream provider (issuer). 
  */
+//TODO simply some of these with a base class e.g. client context lookups.
 public class InitializeOAuth2ClientAuthenticationContext extends AbstractProfileAction {
 
     /** Class logger. */
     @Nonnull
     private final Logger log = LoggerFactory.getLogger(InitializeOAuth2ClientAuthenticationContext.class);
     
-    /** The resolver to use to find the client authentication for the given (client_id). */
-    @NonnullAfterInit private OAuth2ClientAuthenticationResolver clientAuthenticationResovler;
-    
-    /** The strategy used to lookup or create the {@link OAuth2ClientContext}.*/
-    @Nonnull private Function<ProfileRequestContext, OAuth2ClientContext> oauth2ClientContextLookupStrategy;
-    
-    /** The strategy used to lookup or create the {@link OAuth2ClientContext} for storing the client authentication.*/
-    @Nonnull private 
-    Function<ProfileRequestContext, OAuth2ClientAuthenticationContext> oauth2ClientAuthenticationContextLookupStrategy;
-    
-    /** The stashed OAuth2 client context.*/
-    @Nullable private OAuth2ClientContext oauth2ClientContext;
-    
+
+    /** 
+     * The strategy used to lookup or create the {@link OAuth2ClientAuthenticationContext} 
+     * for storing the client authentication.*/
+    @Nonnull private Function<ProfileRequestContext, OAuth2ClientAuthenticationContext> 
+                                                    oauth2ClientAuthenticationContextLookupStrategy;
+        
     /** The stashed OAuth2 client authentication context.*/
     @Nullable private OAuth2ClientAuthenticationContext oauth2ClientAuthenticationContext;
+    
+    /** Lookup function for relying party context. */
+    @Nonnull private Function<ProfileRequestContext,RelyingPartyContext> relyingPartyContextLookupStrategy;
+    
+    /** Applicable stashed profile configuration. */
+    @Nullable private OIDCAuthorizationConfiguration profileConfiguration;
 
     
     /** Constructor.*/
     public InitializeOAuth2ClientAuthenticationContext() {       
-        // Default under OIDCPeerEntityContext in the outbound context (create true).
-        oauth2ClientContextLookupStrategy = new ChildContextLookup<>(OAuth2ClientContext.class).compose(
-                new ChildContextLookup<>(OIDCPeerEntityContext.class).compose(
-                        new OutboundMessageContextLookup()));
-        
+
+        // Default under the OIDC Peer Entity Context, create is true
         oauth2ClientAuthenticationContextLookupStrategy  = 
                 new ChildContextLookup<>(OAuth2ClientAuthenticationContext.class, true).compose(
                 new ChildContextLookup<>(OIDCPeerEntityContext.class).compose(
                         new OutboundMessageContextLookup()));
+        
+        relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
 
     }
 
     @Override
     protected void doInitialize() throws ComponentInitializationException {
         super.doInitialize();
-        
-        if (clientAuthenticationResovler == null) {
-            throw new ComponentInitializationException("OAuth2 Client Authentication Resovler cannot be null");
-        }
-    }
-
-    /**
-     * Set the OAuth2 client authentication resolver.
-     * 
-     * @param resolver the resolver
-     */
-    public void setClientAuthenticationResolver(@Nonnull final OAuth2ClientAuthenticationResolver resolver) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
-        
-        clientAuthenticationResovler = Constraint.isNotNull(resolver, 
-                "OAuth2 Client Authentication Resovler cannot be null");
     }
     
     /**
-     * Set the strategy to lookup the {@link OAuth2ClientContext} 
-     * from the {@link ProfileRequestContext}.
+     * Set lookup strategy for relying party context.
      * 
-     * @param strgy the strategy.
+     * @param strategy  lookup strategy
      */
-    public void setOAuth2ClientContextLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext, OAuth2ClientContext> strgy) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
-
-        oauth2ClientContextLookupStrategy = Constraint.isNotNull(strgy, 
-                "OAuth2 client context lookup strategy cannot be null");
+    public void setRelyingPartyContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,RelyingPartyContext> strategy) {
+        relyingPartyContextLookupStrategy =
+                Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
     }
     
+    
     /**
      * Set the strategy to lookup the {@link OAuth2ClientAuthenticationContext} 
      * from the {@link ProfileRequestContext}.
@@ -147,12 +122,6 @@ public class InitializeOAuth2ClientAuthenticationContext extends AbstractProfile
             return false;
         }
         
-        oauth2ClientContext = oauth2ClientContextLookupStrategy.apply(profileRequestContext);
-        if (oauth2ClientContext == null) {
-            log.error("{} No OAuth2 client context found or created", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
-            return false;
-        }
         oauth2ClientAuthenticationContext = 
                 oauth2ClientAuthenticationContextLookupStrategy.apply(profileRequestContext);
         if (oauth2ClientAuthenticationContext == null) {
@@ -161,6 +130,17 @@ public class InitializeOAuth2ClientAuthenticationContext extends AbstractProfile
             return false;
         }
         
+        final RelyingPartyContext rpCtx = relyingPartyContextLookupStrategy.apply(profileRequestContext);     
+        if (rpCtx != null && rpCtx.getConfiguration() != null &&
+                rpCtx.getProfileConfig() instanceof OIDCAuthorizationConfiguration) {
+            profileConfiguration = (OIDCAuthorizationConfiguration) rpCtx.getProfileConfig();
+        }
+        if (profileConfiguration == null) {
+            log.error("{} OIDCAuthorizationConfiguration not found", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
+            return false;
+        }
+        
         return true;
         
     }
@@ -169,28 +149,17 @@ public class InitializeOAuth2ClientAuthenticationContext extends AbstractProfile
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
         super.doExecute(profileRequestContext);
         
-        try {
-            final ClientAuthentication clientAuth = 
-                    clientAuthenticationResovler.resolveSingle(
-                            new CriteriaSet(new ClientIDCriterion(new ClientID(oauth2ClientContext.getClientId()))));
-            
-            if (clientAuth == null) {
-                log.error("{} No client authentication mode found from resolver", getLogPrefix());
-                ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CONFIG);
-                return;
-            }
-            
-            log.trace("{} Found client authentication mode '{}' for client '{}'", 
-                    getLogPrefix(), clientAuth.getMethod(), oauth2ClientContext.getClientId());
-            
-            oauth2ClientAuthenticationContext.setClientAuthentication(clientAuth);           
-            log.debug("{} Initialized OAuth2 Client Authentication Context for client '{}'", 
-                    getLogPrefix(), oauth2ClientContext.getClientId());
-        } catch (final ResolverException e) {
-            log.warn("{} client context could not be initialized", getLogPrefix(), e);
+        final ClientAuthentication clientAuth = profileConfiguration.getClientAuthentication(profileRequestContext);
+        
+        if (clientAuth == null) {
+            log.error("{} No client authentication mode found from profile configuration", getLogPrefix());
             ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CONFIG);
             return;
         }
+        
+        oauth2ClientAuthenticationContext.setClientAuthentication(clientAuth);   
+        log.debug("{} Initialized OAuth2 Client Authentication Context: Found client authentication mode "
+                + "'{}' for client '{}'",getLogPrefix(), clientAuth.getMethod(), clientAuth.getClientID());
     }
     
     
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientContext.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientContext.java
index 6415634..fab32ee 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientContext.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientContext.java
@@ -18,9 +18,6 @@
 package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
 
 import java.net.URI;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
@@ -34,26 +31,18 @@ import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
-import com.nimbusds.oauth2.sdk.id.ClientID;
-
 import net.shibboleth.idp.plugin.authn.oidc.rp.config.navigate.RedirectUriLookupFunction;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
 import net.shibboleth.idp.profile.AbstractProfileAction;
 import net.shibboleth.idp.profile.IdPEventIds;
-import net.shibboleth.oidc.metadata.criterion.ClientIDCriterion;
-import net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver;
-import net.shibboleth.oidc.security.impl.OAuth2ClientIdentifierResolver;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
 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.primitive.StringSupport;
-import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
-import net.shibboleth.utilities.java.support.resolver.ResolverException;
 
 /**
  * An {@link AbstractProfileAction action} that resolves the client identifier for the chosen 
@@ -65,9 +54,6 @@ public class InitializeOAuth2ClientContext extends AbstractProfileAction {
     @Nonnull
     private final Logger log = LoggerFactory.getLogger(InitializeOAuth2ClientContext.class);
     
-    /** The resolver to use to find the client identifier (client_id). */
-    @NonnullAfterInit private OAuth2ClientIdentifierResolver clientIdResovler;
-    
     /** The strategy used to lookup or create the {@link OAuth2ClientContext} for storing the client_id.*/
     @NonnullAfterInit private Function<ProfileRequestContext, OAuth2ClientContext> oauth2ClientContextLookupStrategy;
     
@@ -77,6 +63,12 @@ public class InitializeOAuth2ClientContext extends AbstractProfileAction {
     /** A redirect_uri lookup strategy which can pull out an override redirect_uri from the profile request context.*/
     @Nonnull private  Function<ProfileRequestContext, URI> redirectUriOverrideLookupStrategy;
     
+    /** Lookup function for relying party context. */
+    @Nonnull private Function<ProfileRequestContext,RelyingPartyContext> relyingPartyContextLookupStrategy;
+    
+    /** Applicable stashed profile configuration. */
+    @Nullable private OIDCAuthorizationConfiguration profileConfiguration;
+    
     /** Constructor.*/
     public InitializeOAuth2ClientContext() {       
         // Default under OIDCPeerEntityContext in the outbound context (create true).
@@ -84,9 +76,22 @@ public class InitializeOAuth2ClientContext extends AbstractProfileAction {
                 new ChildContextLookup<>(OIDCPeerEntityContext.class).compose(
                         new OutboundMessageContextLookup()));
         
+        relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
+        
         redirectUriOverrideLookupStrategy = new RedirectUriLookupFunction();
     }
     
+    /**
+     * Set lookup strategy for relying party context.
+     * 
+     * @param strategy  lookup strategy
+     */
+    public void setRelyingPartyContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,RelyingPartyContext> strategy) {
+        relyingPartyContextLookupStrategy =
+                Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
+    }
+    
     /**
      * Set the redirect_uri lookup strategy to locate an explicitly set redirect_uri.
      * 
@@ -104,24 +109,7 @@ public class InitializeOAuth2ClientContext extends AbstractProfileAction {
     @Override
     protected void doInitialize() throws ComponentInitializationException {
         super.doInitialize();
-        
-        if (clientIdResovler == null) {
-            throw new ComponentInitializationException("OAuth2 Client Resovler cannot be null");
-        }
-    }
-    
-    /**
-     * Set the OAuth2 client identifier (client_id) resolver.
-     * 
-     * @param resolver the resolver
-     */
-    public void setClientIdResolver(@Nonnull final OAuth2ClientIdentifierResolver resolver) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
-        
-        clientIdResovler = Constraint.isNotNull(resolver, "OAuth2 Client Resovler cannot be null");
     }
-
     
     /**
      * Set the strategy to lookup the {@link OAuth2ClientContext} 
@@ -152,33 +140,39 @@ public class InitializeOAuth2ClientContext extends AbstractProfileAction {
             return false;
         }
         
+        final RelyingPartyContext rpCtx = relyingPartyContextLookupStrategy.apply(profileRequestContext);     
+        if (rpCtx != null && rpCtx.getConfiguration() != null &&
+                rpCtx.getProfileConfig() instanceof OIDCAuthorizationConfiguration) {
+            profileConfiguration = (OIDCAuthorizationConfiguration) rpCtx.getProfileConfig();
+        }
+        if (profileConfiguration == null) {
+            log.error("{} OIDCAuthorizationConfiguration not found", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
+            return false;
+        }
+        
         return true;        
     }
     
     @Override
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
         super.doExecute(profileRequestContext);
-
-        try {
-            final String clientId = clientIdResovler.resolveSingle(profileRequestContext);
-            if (clientId == null) {
-                log.error("{} No client_id found from resolver", getLogPrefix());
-                ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CONFIG);
-                return;
-            }
-            oauth2ClientContext.setClientId(clientId);
-            
-            final URI redirectUri = redirectUriOverrideLookupStrategy.apply(profileRequestContext);
-            if (redirectUri != null) {
-                log.debug("{} Redirect_uri has been explicitly set as '{}'", getLogPrefix(), redirectUri);
-                oauth2ClientContext.setRedirectUriOverride(redirectUri);
-            }             
-            log.debug("{} Initialized OAuth2 Client Context for client '{}'", getLogPrefix(), clientId);
-        } catch (final ResolverException e) {
-            log.warn("{} client context could not be initialized", getLogPrefix(), e);
+        
+        final String clientId = profileConfiguration.getClientId(profileRequestContext);
+        if (StringSupport.trimOrNull(clientId) == null) {
+            log.error("{} No client_id found from profile configuration", getLogPrefix());
             ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CONFIG);
             return;
         }
+        oauth2ClientContext.setClientId(clientId);
+        
+        final URI redirectUri = redirectUriOverrideLookupStrategy.apply(profileRequestContext);
+        if (redirectUri != null) {
+            log.debug("{} Redirect_uri has been explicitly set as '{}'", getLogPrefix(), redirectUri);
+            oauth2ClientContext.setRedirectUriOverride(redirectUri);
+        }             
+        log.debug("{} Initialized OAuth2 Client Context for client '{}'", getLogPrefix(), clientId);
+       
     }
     
     
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientAuthenticationContainer.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientAuthenticationContainer.java
deleted file mode 100644
index 463c1af..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientAuthenticationContainer.java
+++ /dev/null
@@ -1,147 +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.spring.impl;
-
-import javax.annotation.Nonnull;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Objects;
-
-import net.shibboleth.ext.spring.service.AbstractServiceableComponent;
-import net.shibboleth.oidc.metadata.ProviderMetadataResolver;
-import net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver;
-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;
-
-/**
- * This class is a sortable container of {@link OAuth2ClientAuthenticationResolver}s, wrapped into a serviceable component.
- */
-public class OAuth2ClientAuthenticationContainer extends AbstractServiceableComponent<OAuth2ClientAuthenticationResolver>
-                                               implements Comparable<OAuth2ClientAuthenticationContainer> {
-
-    /** If we autogenerate a sort key it comes from this count. */
-    private static int sortKeyValue;
-
-    /** Class logger. */
-    private final Logger log = LoggerFactory.getLogger(OAuth2ClientAuthenticationContainer.class);
-
-    /** The embedded resolver. */
-    @NonnullAfterInit
-    private OAuth2ClientAuthenticationResolver resolver;
-
-    /** The key by which we sort the provider. */
-    @NonnullAfterInit
-    private Integer sortKey;
-
-    /**
-     * Set the sort key.
-     * 
-     * @param key what to set
-     */
-    public void setSortKey(final int key) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        sortKey = key;
-    }
-
-    /**
-     * Set the {@link ProviderMetadataResolver} to embed.
-     * 
-     * @param theResolver The {@link ProviderMetadataResolver} to embed.
-     */
-    @Nonnull
-    public void setEmbeddedResolver(@Nonnull final OAuth2ClientAuthenticationResolver theResolver) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        resolver = Constraint.isNotNull(theResolver, "OAuth2ClientAuthenticationResolver cannot be null");
-    }
-
-    /**
-     * Return what we are build around. Used for testing.
-     * 
-     * @return the parameter we got as a constructor
-     */
-    @Nonnull
-    public OAuth2ClientAuthenticationResolver getEmbeddedResolver() {
-        return resolver;
-    }
-
-
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        setId(resolver.getId());
-        super.doInitialize();
-        if (null == resolver) {
-            throw new ComponentInitializationException("OAuth2ClientAuthenticationResolver cannot be null");
-        }
-
-        if (null == sortKey) {
-            synchronized (OAuth2ClientAuthenticationContainer.class) {
-                sortKeyValue++;
-                setSortKey(sortKeyValue);
-            }
-            log.info("Top level OAuth2ClientAuthentication Provider '{}' "
-                    + "did not have a sort key; giving it value '{}'", getId(),
-                    sortKey);
-        }
-    }
-    
-
-    @Override
-    @Nonnull
-    public OAuth2ClientAuthenticationResolver getComponent() {
-        return getEmbeddedResolver();
-    }
-
-    @Override
-    public int compareTo(final OAuth2ClientAuthenticationContainer other) {
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-        final int result = sortKey.compareTo(other.sortKey);
-        if (result != 0) {
-            return result;
-        }
-        if (equals(other)) {
-            return 0;
-        }
-        return getId().compareTo(other.getId());
-    }
-
-    /**
-     * {@inheritDoc}. We are within a spring context and so equality can be determined by ID, however we also test by
-     * sortKey just in case.
-     */
-    @Override
-    public boolean equals(final Object other) {
-        if (null == other) {
-            return false;
-        }
-        if (!(other instanceof OAuth2ClientAuthenticationContainer)) {
-            return false;
-        }
-        final OAuth2ClientAuthenticationContainer otherRp = (OAuth2ClientAuthenticationContainer) other;
-
-        return Objects.equal(otherRp.sortKey, sortKey) && Objects.equal(getId(), otherRp.getId());
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(sortKey, getId());
-    }
-}
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientAuthenticationResolverServiceStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientAuthenticationResolverServiceStrategy.java
deleted file mode 100644
index 7b7ffaa..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientAuthenticationResolverServiceStrategy.java
+++ /dev/null
@@ -1,65 +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.spring.impl;
-
-import java.util.Collection;
-import java.util.function.Function;
-
-import javax.annotation.Nullable;
-
-import org.springframework.beans.factory.BeanCreationException;
-import org.springframework.context.ApplicationContext;
-
-import net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver;
-import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.service.ServiceException;
-import net.shibboleth.utilities.java.support.service.ServiceableComponent;
-
-public class OAuth2ClientAuthenticationResolverServiceStrategy extends AbstractIdentifiableInitializableComponent
-        implements Function<ApplicationContext, ServiceableComponent<OAuth2ClientAuthenticationResolver>> {
-
-    @Override
-    public ServiceableComponent<OAuth2ClientAuthenticationResolver> apply(
-            @Nullable final ApplicationContext appContext) {
-        final Collection<OAuth2ClientAuthenticationContainer> resolvers =
-                appContext.getBeansOfType(OAuth2ClientAuthenticationContainer.class).values();
-
-        if (resolvers.isEmpty()) {
-            throw new ServiceException(
-                    "Reload did not produce any bean of type " + OAuth2ClientAuthenticationContainer.class.getName());
-        }
-        if (1 == resolvers.size()) {
-            // done
-            return resolvers.iterator().next();
-        }
-        // initialize so we can sort
-        for (final OAuth2ClientAuthenticationContainer resolver : resolvers) {
-            try {
-                resolver.initialize();
-            } catch (final ComponentInitializationException e) {
-                throw new BeanCreationException("Could not preinitialize " 
-                        + resolver.getId(), e);
-            }
-        }
-
-        throw new BeanCreationException("could not preinitialize the Client Authentication Provider, "
-                + "mulitple Client Authentication Providers not supported");
-    }
-
-}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientIdentifierContainer.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientIdentifierContainer.java
deleted file mode 100644
index 601200d..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientIdentifierContainer.java
+++ /dev/null
@@ -1,148 +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.spring.impl;
-
-import javax.annotation.Nonnull;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Objects;
-
-import net.shibboleth.ext.spring.service.AbstractServiceableComponent;
-import net.shibboleth.oidc.metadata.ProviderMetadataResolver;
-import net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver;
-import net.shibboleth.oidc.security.impl.OAuth2ClientIdentifierResolver;
-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;
-
-/**
- * This class is a sortable container of {@link OAuth2ClientAuthenticationResolver}s, wrapped into a serviceable component.
- */
-public class OAuth2ClientIdentifierContainer extends AbstractServiceableComponent<OAuth2ClientIdentifierResolver>
-                                               implements Comparable<OAuth2ClientIdentifierContainer> {
-
-    /** If we autogenerate a sort key it comes from this count. */
-    private static int sortKeyValue;
-
-    /** Class logger. */
-    private final Logger log = LoggerFactory.getLogger(OAuth2ClientIdentifierContainer.class);
-
-    /** The embedded resolver. */
-    @NonnullAfterInit
-    private OAuth2ClientIdentifierResolver resolver;
-
-    /** The key by which we sort the provider. */
-    @NonnullAfterInit
-    private Integer sortKey;
-
-    /**
-     * Set the sort key.
-     * 
-     * @param key what to set
-     */
-    public void setSortKey(final int key) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        sortKey = key;
-    }
-
-    /**
-     * Set the {@link OAuth2ClientIdentifierResolver} to embed.
-     * 
-     * @param theResolver The {@link OAuth2ClientIdentifierResolver} to embed.
-     */
-    @Nonnull
-    public void setEmbeddedResolver(@Nonnull final OAuth2ClientIdentifierResolver theResolver) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        resolver = Constraint.isNotNull(theResolver, "OAuth2ClientAuthenticationResolver cannot be null");
-    }
-
-    /**
-     * Return what we are built around. Used for testing.
-     * 
-     * @return the embedded resolver.
-     */
-    @Nonnull
-    public OAuth2ClientIdentifierResolver getEmbeddedResolver() {
-        return resolver;
-    }
-
-
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        setId(resolver.getId());
-        super.doInitialize();
-        if (null == resolver) {
-            throw new ComponentInitializationException("OAuth2ClientAuthenticationResolver cannot be null");
-        }
-
-        if (null == sortKey) {
-            synchronized (OAuth2ClientIdentifierContainer.class) {
-                sortKeyValue++;
-                setSortKey(sortKeyValue);
-            }
-            log.info("Top level OAuth2ClientAuthentication Provider '{}' "
-                    + "did not have a sort key; giving it value '{}'", getId(),
-                    sortKey);
-        }
-    }
-    
-
-    @Override
-    @Nonnull
-    public OAuth2ClientIdentifierResolver getComponent() {
-        return getEmbeddedResolver();
-    }
-
-    @Override
-    public int compareTo(final OAuth2ClientIdentifierContainer other) {
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-        final int result = sortKey.compareTo(other.sortKey);
-        if (result != 0) {
-            return result;
-        }
-        if (equals(other)) {
-            return 0;
-        }
-        return getId().compareTo(other.getId());
-    }
-
-    /**
-     * {@inheritDoc}. We are within a spring context and so equality can be determined by ID, however we also test by
-     * sortKey just in case.
-     */
-    @Override
-    public boolean equals(final Object other) {
-        if (null == other) {
-            return false;
-        }
-        if (!(other instanceof OAuth2ClientIdentifierContainer)) {
-            return false;
-        }
-        final OAuth2ClientIdentifierContainer otherRp = (OAuth2ClientIdentifierContainer) other;
-
-        return Objects.equal(otherRp.sortKey, sortKey) && Objects.equal(getId(), otherRp.getId());
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(sortKey, getId());
-    }
-}
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientIdentifierResolverServiceStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientIdentifierResolverServiceStrategy.java
deleted file mode 100644
index 0820475..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientIdentifierResolverServiceStrategy.java
+++ /dev/null
@@ -1,67 +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.spring.impl;
-
-import java.util.Collection;
-import java.util.function.Function;
-
-import javax.annotation.Nullable;
-
-import org.springframework.beans.factory.BeanCreationException;
-import org.springframework.context.ApplicationContext;
-
-import net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver;
-import net.shibboleth.oidc.security.impl.OAuth2ClientIdentifierResolver;
-import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.service.ServiceException;
-import net.shibboleth.utilities.java.support.service.ServiceableComponent;
-
-public class OAuth2ClientIdentifierResolverServiceStrategy extends AbstractIdentifiableInitializableComponent
-        implements Function<ApplicationContext, ServiceableComponent<OAuth2ClientIdentifierResolver>> {
-
-    @Override
-    public ServiceableComponent<OAuth2ClientIdentifierResolver> apply(
-            @Nullable final ApplicationContext appContext) {
-        
-        final Collection<OAuth2ClientIdentifierContainer> resolvers =
-                appContext.getBeansOfType(OAuth2ClientIdentifierContainer.class).values();
-
-        if (resolvers.isEmpty()) {
-            throw new ServiceException(
-                    "Reload did not produce any bean of type " + OAuth2ClientAuthenticationContainer.class.getName());
-        }
-        if (1 == resolvers.size()) {
-            // done
-            return resolvers.iterator().next();
-        }
-        // initialize so we can sort
-        for (final OAuth2ClientIdentifierContainer resolver : resolvers) {
-            try {
-                resolver.initialize();
-            } catch (final ComponentInitializationException e) {
-                throw new BeanCreationException("Could not preinitialize the Client Identifer Provider " 
-                        + resolver.getId(), e);
-            }
-        }
-
-        throw new BeanCreationException("Could not preinitialize the Client Identifer Provider"
-                + ", mulitple client identifer providers not supported");
-    }
-
-}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/ReloadingOAuth2ClientAuthenticationProvider.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/ReloadingOAuth2ClientAuthenticationProvider.java
deleted file mode 100644
index 62dee46..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/ReloadingOAuth2ClientAuthenticationProvider.java
+++ /dev/null
@@ -1,107 +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.spring.impl;
-
-import java.util.Collections;
-
-import javax.annotation.Nonnull;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
-
-import net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver;
-import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
-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;
-import net.shibboleth.utilities.java.support.service.ReloadableService;
-import net.shibboleth.utilities.java.support.service.ServiceableComponent;
-
-
-/**
- * A service interface to implement the {@link OAuth2ClientAuthenticationResolver}.
- */
-public class ReloadingOAuth2ClientAuthenticationProvider extends AbstractIdentifiableInitializableComponent 
-    implements OAuth2ClientAuthenticationResolver {
-    
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(ReloadingOAuth2ClientAuthenticationProvider.class);
-    
-    /** The service which manages the reloading. */
-    private final ReloadableService<OAuth2ClientAuthenticationResolver> service;
-    
-    /**
-     * Constructor.
-     * 
-     * @param resolverService the service which will manage the loading.
-     */
-    public ReloadingOAuth2ClientAuthenticationProvider(
-            @Nonnull final ReloadableService<OAuth2ClientAuthenticationResolver> resolverService) {
-        service = Constraint.isNotNull(resolverService, "ProviderMetadataResolver Service cannot be null");
-    }
-
-    @Override
-    public Iterable<ClientAuthentication> resolve(@Nonnull final CriteriaSet criteria) throws ResolverException {
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-        ServiceableComponent<OAuth2ClientAuthenticationResolver> component = null;
-        try {
-            component = service.getServiceableComponent();
-            if (null == component) {
-                log.error("OAuth2ClientAuthenticationResolver '{}': Error accessing underlying source: "
-                        + "Invalid configuration.", getId());
-            } else {
-                final OAuth2ClientAuthenticationResolver resolver = component.getComponent();
-                return resolver.resolve(criteria);
-            }
-        } catch (final ResolverException e) {
-            log.error("OAuth2ClientAuthenticationResolver '{}': Error during resolution", getId(), e);
-        } finally {
-            if (null != component) {
-                component.unpinComponent();
-            }
-        }
-        return Collections.emptySet();
-    }
-
-    @Override
-    public ClientAuthentication resolveSingle(@Nonnull final CriteriaSet criteria) throws ResolverException {
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-        ServiceableComponent<OAuth2ClientAuthenticationResolver> component = null;
-        try {
-            component = service.getServiceableComponent();
-            if (null == component) {
-                log.error("ReloadingProviderMetadataProvider '{}': Error accessing underlying source: "
-                        + "Invalid configuration.", getId());
-            } else {
-                final OAuth2ClientAuthenticationResolver resolver = component.getComponent();
-                return resolver.resolveSingle(criteria);
-            }
-        } catch (final ResolverException e) {
-            log.error("ProviderMetadataResolver '{}': Error during resolution", getId(), e);
-        } finally {
-            if (null != component) {
-                component.unpinComponent();
-            }
-        }
-        return null;
-    }
-
-}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/ReloadingOAuth2ClientIdentifierProvider.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/ReloadingOAuth2ClientIdentifierProvider.java
deleted file mode 100644
index 5d4ee68..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/ReloadingOAuth2ClientIdentifierProvider.java
+++ /dev/null
@@ -1,105 +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.spring.impl;
-
-import java.util.Collections;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import net.shibboleth.oidc.security.impl.OAuth2ClientIdentifierResolver;
-import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.resolver.ResolverException;
-import net.shibboleth.utilities.java.support.service.ReloadableService;
-import net.shibboleth.utilities.java.support.service.ServiceableComponent;
-
-
-/**
- * A service interface to implement the {@link OAuth2ClientIdentifierResolver}.
- */
-public class ReloadingOAuth2ClientIdentifierProvider extends AbstractIdentifiableInitializableComponent 
-    implements OAuth2ClientIdentifierResolver {
-    
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(ReloadingOAuth2ClientIdentifierProvider.class);
-    
-    /** The service which manages the reloading. */
-    private final ReloadableService<OAuth2ClientIdentifierResolver> service;
-    
-    /**
-     * Constructor.
-     * 
-     * @param resolverService the service which will manage the loading.
-     */
-    public ReloadingOAuth2ClientIdentifierProvider(
-            @Nonnull final ReloadableService<OAuth2ClientIdentifierResolver> resolverService) {
-        service = Constraint.isNotNull(resolverService, "ProviderMetadataResolver Service cannot be null");
-    }
-
-    @Override
-    public Iterable<String> resolve(@Nonnull final ProfileRequestContext criteria) throws ResolverException {
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-        ServiceableComponent<OAuth2ClientIdentifierResolver> component = null;
-        try {
-            component = service.getServiceableComponent();
-            if (null == component) {
-                log.error("OAuth2ClientAuthenticationResolver '{}': Error accessing underlying source: "
-                        + "Invalid configuration.", getId());
-            } else {
-                final OAuth2ClientIdentifierResolver resolver = component.getComponent();
-                return resolver.resolve(criteria);
-            }
-        } catch (final ResolverException e) {
-            log.error("OAuth2ClientAuthenticationResolver '{}': Error during resolution", getId(), e);
-        } finally {
-            if (null != component) {
-                component.unpinComponent();
-            }
-        }
-        return Collections.emptySet();
-    }
-
-    @Override
-    public String resolveSingle(@Nonnull final ProfileRequestContext criteria) throws ResolverException {
-        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
-        ServiceableComponent<OAuth2ClientIdentifierResolver> component = null;
-        try {
-            component = service.getServiceableComponent();
-            if (null == component) {
-                log.error("ReloadingProviderMetadataProvider '{}': Error accessing underlying source: "
-                        + "Invalid configuration.", getId());
-            } else {
-                final OAuth2ClientIdentifierResolver resolver = component.getComponent();
-                return resolver.resolveSingle(criteria);
-            }
-        } catch (final ResolverException e) {
-            log.error("ProviderMetadataResolver '{}': Error during resolution", getId(), e);
-        } finally {
-            if (null != component) {
-                component.unpinComponent();
-            }
-        }
-        return null;
-    }
-
-}
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 f9b0fc8..1727532 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
@@ -9,70 +9,63 @@
 
     default-init-method="initialize" default-destroy-method="destroy">
 
-    <!-- 
-    System beans needed for extension to function, loaded after global.xml.
-    The default template shows an incomplete example authentication flow descriptor which can be
-    removed if not needed 
-    -->
-    
+    <!-- System beans needed for extension to function, loaded after global.xml. The default template shows an incomplete 
+        example authentication flow descriptor which can be removed if not needed -->
+
     <!-- Functions use by the flow and global beans -->
-       
+
     <bean id="shibboleth.ChildLookup.OIDCProviderMetadataContext"
         class="org.opensaml.messaging.context.navigate.ChildContextLookup"
         c:type="#{ T(net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext) }" />
-        
+
     <bean id="shibboleth.ChildLookup.OAuth2ClientContext"
         class="org.opensaml.messaging.context.navigate.ChildContextLookup"
         c:type="#{ T(net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext) }" />
-                       
+
     <bean id="shibboleth.ChildLookup.OIDCPeerEntityContext"
         class="org.opensaml.messaging.context.navigate.ChildContextLookup"
         c:type="#{ T(net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext) }" />
-        
+
     <bean id="shibboleth.ChildLookup.OIDCProviderMetadataContextFromOutbound" parent="shibboleth.Functions.Compose">
         <constructor-arg name="g">
-            <ref bean="shibboleth.ChildLookup.OIDCProviderMetadataContext"/>
+            <ref bean="shibboleth.ChildLookup.OIDCProviderMetadataContext" />
         </constructor-arg>
         <constructor-arg name="f">
-            <ref bean="shibboleth.ChildLookup.OIDCPeerEntityFromOutbound"/>
+            <ref bean="shibboleth.ChildLookup.OIDCPeerEntityFromOutbound" />
         </constructor-arg>
     </bean>
-    
+
     <bean id="shibboleth.ChildLookup.OAuth2ClientContextFromOutbound" parent="shibboleth.Functions.Compose">
         <constructor-arg name="g">
-            <ref bean="shibboleth.ChildLookup.OAuth2ClientContext"/>
+            <ref bean="shibboleth.ChildLookup.OAuth2ClientContext" />
         </constructor-arg>
         <constructor-arg name="f">
-            <ref bean="shibboleth.ChildLookup.OIDCPeerEntityFromOutbound"/>
+            <ref bean="shibboleth.ChildLookup.OIDCPeerEntityFromOutbound" />
         </constructor-arg>
     </bean>
-    
+
     <!-- Find OIDCPeerEntity Context under outbound message -->
     <bean id="shibboleth.ChildLookup.OIDCPeerEntityFromOutbound" parent="shibboleth.Functions.Compose"
-                c:g-ref="shibboleth.ChildLookup.OIDCPeerEntityContext"
-                c:f-ref="shibboleth.MessageContextLookup.Outbound" />
-        
-        
+        c:g-ref="shibboleth.ChildLookup.OIDCPeerEntityContext" c:f-ref="shibboleth.MessageContextLookup.Outbound" />
+
+
     <!-- The authentication flow descriptor -->
-  
+
     <bean id="authn/OIDCRelyingParty" parent="shibboleth.AuthenticationFlow"
-            p:order="%{idp.authn.oidc.rp.order:1000}"
-            p:nonBrowserSupported="%{idp.authn.oidc.rp.nonBrowserSupported:true}"
-            p:passiveAuthenticationSupported="%{idp.authn.oidc.rp.passiveAuthenticationSupported:true}"
-            p:forcedAuthenticationSupported="%{idp.authn.oidc.rp.forcedAuthenticationSupported:true}"
-            p:proxyRestrictionsEnforced="%{idp.authn.oidc.rp.proxyRestrictionsEnforced:%{idp.authn.enforceProxyRestrictions:true}}"
-            p:proxyScopingEnforced="%{idp.authn.oidc.rp.proxyScopingEnforced:false}"
-            p:discoveryRequired="%{idp.authn.oidc.rp.discoveryRequired:false}"
-            p:lifetime="%{idp.authn.oidc.rp.lifetime:%{idp.authn.defaultLifetime:PT1H}}"
-            p:inactivityTimeout="%{idp.authn.oidc.rp.inactivityTimeout:%{idp.authn.defaultTimeout:PT30M}}"
-            p:reuseCondition-ref="#{'%{idp.authn.oidc.rp.reuseCondition:shibboleth.Conditions.TRUE}'.trim()}"
-            p:activationCondition-ref="#{'%{idp.authn.oidc.rp.activationCondition:shibboleth.Conditions.TRUE}'.trim()}">
+        p:order="%{idp.authn.oidc.rp.order:1000}" p:nonBrowserSupported="%{idp.authn.oidc.rp.nonBrowserSupported:true}"
+        p:passiveAuthenticationSupported="%{idp.authn.oidc.rp.passiveAuthenticationSupported:true}"
+        p:forcedAuthenticationSupported="%{idp.authn.oidc.rp.forcedAuthenticationSupported:true}"
+        p:proxyRestrictionsEnforced="%{idp.authn.oidc.rp.proxyRestrictionsEnforced:%{idp.authn.enforceProxyRestrictions:true}}"
+        p:proxyScopingEnforced="%{idp.authn.oidc.rp.proxyScopingEnforced:false}"
+        p:discoveryRequired="%{idp.authn.oidc.rp.discoveryRequired:false}"
+        p:lifetime="%{idp.authn.oidc.rp.lifetime:%{idp.authn.defaultLifetime:PT1H}}"
+        p:inactivityTimeout="%{idp.authn.oidc.rp.inactivityTimeout:%{idp.authn.defaultTimeout:PT30M}}"
+        p:reuseCondition-ref="#{'%{idp.authn.oidc.rp.reuseCondition:shibboleth.Conditions.TRUE}'.trim()}"
+        p:activationCondition-ref="#{'%{idp.authn.oidc.rp.activationCondition:shibboleth.Conditions.TRUE}'.trim()}">
         <property name="supportedPrincipals">
             <list>
-                <bean parent="shibboleth.SAML2AuthnContextClassRef"
-                    c:classRef="class-ref" />
-                <bean parent="shibboleth.SAML1AuthenticationMethod"
-                    c:method="auth-ref" />
+                <bean parent="shibboleth.SAML2AuthnContextClassRef" c:classRef="class-ref" />
+                <bean parent="shibboleth.SAML1AuthenticationMethod" c:method="auth-ref" />
             </list>
         </property>
         <property name="supportedPrincipalsByString">
@@ -80,63 +73,19 @@
                 c:_0="#{'%{idp.authn.oidc.rp.supportedPrincipals:}'.trim()}" />
         </property>
     </bean>
-    
+
     <bean id="issuer" class="java.lang.String" c:_0="%{idp.authn.oidc.rp.issuer:%{idp.entityID}}" />
-    
-    <bean id="AbstractOIDCProfile" abstract="true"
-        p:securityConfiguration-ref="%{idp.security.authn.oidc.rp.config:shibboleth.oidc.DefaultSecurityConfiguration}" />
-        
-    <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:refreshTokenLifetime="%{idp.authn.oidc.rp.refreshToken.defaultLifetime:PT2H}"
-        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:deniedUserInfoAttributes="%{idp.authn.oidc.rp.deniedUserInfoAttributes:}" />
-   
-    <!--
-    Security Configuration Defaults. These settings establish the default security
-    configurations for signatures and loads the default credentials used.
-    -->
-
-    <bean id="shibboleth.oidc.DefaultSecurityConfiguration"
-        class="net.shibboleth.oidc.profile.config.OIDCSecurityConfiguration">
-        <!-- Add these back were appropriate -->
-       <!--  <property name="signatureSigningConfiguration">
-            <ref bean="#{'%{idp.oidc.signing.config:shibboleth.oidc.SigningConfiguration}'.trim()}" />
-        </property>
-        <property name="encryptionConfiguration">
-            <ref bean="#{'%{idp.oidc.encryption.config:shibboleth.oidc.EncryptionConfiguration}'.trim()}" />
-        </property>
-        <property name="requestObjectDecryptionConfiguration">
-            <ref bean="#{'%{idp.oidc.rodecrypt.config:shibboleth.oidc.requestObjectDecryptionConfiguration}'.trim()}" />
-        </property>
-        <property name="requestObjectSignatureValidationConfiguration">
-            <ref bean="#{'%{idp.oidc.rovalid.config:shibboleth.oidc.requestObjectSignatureValidationConfiguration}'.trim()}" />
-        </property>
-        <property name="tokenEndpointJwtSignatureValidationConfiguration">
-            <ref bean="#{'%{idp.oidc.rovalid.config:shibboleth.oidc.tokenEndpointJwtSignatureValidationConfiguration}'.trim()}" />
-        </property> -->
-    </bean>
-    
+
+
+
     <!-- Necessary for encoder parsing and claims mapping to function. -->
-    
+
     <bean parent="shibboleth.RegistryNamingFunction" c:claz="net.minidev.json.JSONObject">
         <constructor-arg name="function">
             <bean class="net.shibboleth.oidc.attribute.transcoding.AbstractOIDCAttributeTranscoder.NamingFunction" />
-        </constructor-arg>    
+        </constructor-arg>
     </bean>
-    
+
     <!-- Controller implementation -->
     <bean id="shibboleth.authn.OIDC.externalServletPath" class="java.lang.String"
         c:_0="%{idp.authn.oidc.rp.externalAuthnPath:/Authn/OIDC/RP}">
@@ -146,128 +95,60 @@
     <bean id="shibboleth.authn.OIDC.externalAuthnPath" class="java.lang.String"
         c:_0="servletRelative:#{getObject('shibboleth.authn.OIDC.externalServletPath')}#{T(net.shibboleth.idp.plugin.authn.oidc.rp.impl.AuthorizationController).AUTHORIZE_PATH_SEGMENT}" />
 
-    
-    
+
+
     <bean id="shibboleth.oidc.rp.AuthorizationController"
         p:redirectUriCreationStrategy="#{getObject('shibboleth.oidc.rp.RedirectUriCreationStrategy') ?: getObject('shibboleth.oidc.rp.DefaultRedirectUriCreationStrategy')}"
         class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AuthorizationController" />
-        
-        
-    <bean id="shibboleth.oidc.rp.DefaultRedirectUriCreationStrategy" 
+
+    <bean id="shibboleth.oidc.rp.DefaultRedirectUriCreationStrategy"
         c:callbackPath="#{getObject('shibboleth.authn.OIDC.externalServletPath')}/callback"
-        c:allowedOrigins="%{idp.oidc.rp.redirecturl.allowedOrigins:}"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.DefaultRedirectUriCreationFunction"/>
-       
-    <!-- TODO these bean names need to change - otherwise they may class with the OP plugin if installed
-    at the same time -->
-      
+        c:allowedOrigins="%{idp.authn.oidc.rp.client.redirecturl.allowedOrigins:}"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.DefaultRedirectUriCreationFunction" />
+
+
     <!-- OIDC OP information resolver service beans. -->
-    
+
     <bean id="shibboleth.oidc.rp.ProviderMetadataResolver"
         class="net.shibboleth.oidc.metadata.impl.ReloadingProviderMetadataProvider"
         c:resolverService-ref="shibboleth.oidc.rp.ProviderMetadataResolverService" />
-    
-   <!--  TODO add this back? depends-on="shibboleth.AttributeResolverService"-->
+
+    <!-- TODO add this back? depends-on="shibboleth.AttributeResolverService" -->
 
     <bean id="shibboleth.oidc.rp.ProviderMetadataResolverService"
         class="net.shibboleth.ext.spring.service.ReloadableSpringService"
-     
+
         p:serviceConfigurations-ref="ExtendedProviderMetadataResolverResources"
         p:failFast="%{idp.service.providermetadata.failFast:%{idp.service.failFast:false}}"
         p:reloadCheckDelay="%{idp.service.providermetadata.checkInterval:PT0S}"
         p:beanPostProcessors-ref="shibboleth.IdentifiableBeanPostProcessor"
         p:beanFactoryPostProcessors-ref="shibboleth.PropertySourcesPlaceholderConfigurer">
-        <constructor-arg name="claz"
-            value="net.shibboleth.oidc.metadata.ProviderMetadataResolver" />
+        <constructor-arg name="claz" value="net.shibboleth.oidc.metadata.ProviderMetadataResolver" />
         <constructor-arg name="strategy">
-            <bean class="net.shibboleth.oidc.profile.spring.relyingparty.metadata.impl.ProviderMetadataResolverServiceStrategy" />
+            <bean
+                class="net.shibboleth.oidc.profile.spring.relyingparty.metadata.impl.ProviderMetadataResolverServiceStrategy" />
         </constructor-arg>
     </bean>
-    <!-- TODO ADD BACK THE CONDITION (GET TO WORK WITH TEST) <util:list id="shibboleth.DefaultProviderMetadataResolverResources">
-        <value>conditional:%{idp.home}/conf/oidc-providermetadata-resolvers.xml</value>
-        OR <value>classpath:/conf/oidc-providermetadata-resolvers.xml</value>
-    </util:list> -->
+    <!-- TODO ADD BACK THE CONDITION (GET TO WORK WITH TEST) <util:list id="shibboleth.DefaultProviderMetadataResolverResources"> 
+        <value>conditional:%{idp.home}/conf/oidc-providermetadata-resolvers.xml</value> OR <value>classpath:/conf/oidc-providermetadata-resolvers.xml</value> 
+        </util:list> -->
     <util:list id="shibboleth.DefaultProviderMetadataResolverResources">
         <value>%{idp.home}/conf/authn/oidc-providermetadata-resolvers.xml</value> <!-- should be a conditional:? -->
     </util:list>
     <!-- Auto-append system config file to resource set. -->
-    <bean id ="ExtendedProviderMetadataResolverResources" class="net.shibboleth.ext.spring.factory.CombiningListFactoryBean"
-            p:firstList="#{getObject('%{idp.service.providermetadata.resources:shibboleth.ProviderMetadataResolverResources}'.trim()) ?:
+    <bean id="ExtendedProviderMetadataResolverResources"
+        class="net.shibboleth.ext.spring.factory.CombiningListFactoryBean"
+        p:firstList="#{getObject('%{idp.service.providermetadata.resources:shibboleth.ProviderMetadataResolverResources}'.trim()) ?:
                 getObject('shibboleth.DefaultProviderMetadataResolverResources')}">
         <property name="secondList">
-            <util:list >
-                <value>classpath:/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/providermetadata-resolver-system.xml</value>
-            </util:list>
-        </property>
-    </bean>
-    
-    
-    <!-- Client authentication resolvers -->
-    <!--  TODO add this back? depends-on="shibboleth.AttributeResolverService"-->
-    
-    <bean id="shibboleth.oidc.rp.ClientAuthenticationResolverService"
-        class="net.shibboleth.ext.spring.service.ReloadableSpringService"
-        p:serviceConfigurations-ref="ExtendedClientAuthenticationResolverResources"
-        p:failFast="%{idp.service.clientinfo.failFast:%{idp.service.failFast:false}}"
-        p:reloadCheckDelay="%{idp.service.clientinfo.checkInterval:PT0S}"
-        p:beanPostProcessors-ref="shibboleth.IdentifiableBeanPostProcessor"
-        p:beanFactoryPostProcessors-ref="shibboleth.PropertySourcesPlaceholderConfigurer">
-        <constructor-arg name="claz"
-            value="net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver" />
-        <constructor-arg name="strategy">
-            <bean class="net.shibboleth.idp.plugin.authn.oidc.rp.spring.impl.OAuth2ClientAuthenticationResolverServiceStrategy" />
-        </constructor-arg>
-    </bean>
-    
-    <bean id ="ExtendedClientAuthenticationResolverResources" class="net.shibboleth.ext.spring.factory.CombiningListFactoryBean"
-            p:firstList="#{getObject('%{idp.service.clientinfo.resources:shibboleth.ClientInformationResolverResources}'.trim()) ?:
-                getObject('shibboleth.DefaultClientAuthenticationResolverResources')}">
-        <property name="secondList">
-            <util:list >
-                <value>classpath:/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientauthentication-resolver-system.xml</value>
-            </util:list>
-        </property>
-    </bean>
-    <util:list id="shibboleth.DefaultClientAuthenticationResolverResources">
-        <value>%{idp.home}/conf/authn/oidc-client-registration-authentication.xml</value> <!-- should be a conditional:? -->
-    </util:list>
-    
-    <bean id="shibboleth.oidc.rp.OAuth2ClientAuthenticationResolver"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.spring.impl.ReloadingOAuth2ClientAuthenticationProvider"
-        c:resolverService-ref="shibboleth.oidc.rp.ClientAuthenticationResolverService" />
-        
-        
-    <!-- Client Identifier resolvers -->
-    <bean id="shibboleth.oidc.rp.ClientIdentifierResolverService"
-        class="net.shibboleth.ext.spring.service.ReloadableSpringService"
-        p:serviceConfigurations-ref="ExtendedClientIdentifierResolverResources"
-        p:failFast="%{idp.service.clientinfo.failFast:%{idp.service.failFast:false}}"
-        p:reloadCheckDelay="%{idp.service.clientinfo.checkInterval:PT0S}"
-        p:beanPostProcessors-ref="shibboleth.IdentifiableBeanPostProcessor"
-        p:beanFactoryPostProcessors-ref="shibboleth.PropertySourcesPlaceholderConfigurer">
-        <constructor-arg name="claz"
-            value="net.shibboleth.oidc.security.impl.OAuth2ClientIdentifierResolver" />
-        <constructor-arg name="strategy">
-            <bean class="net.shibboleth.idp.plugin.authn.oidc.rp.spring.impl.OAuth2ClientIdentifierResolverServiceStrategy" />
-        </constructor-arg>
-    </bean>
-    
-    <bean id ="ExtendedClientIdentifierResolverResources" class="net.shibboleth.ext.spring.factory.CombiningListFactoryBean"
-            p:firstList="#{getObject('%{idp.service.clientinfo.resources:shibboleth.ClientInformationResolverResources}'.trim()) ?:
-                getObject('shibboleth.DefaultClientIdentifierResolverResources')}">
-        <property name="secondList">
-            <util:list >
-                <value>classpath:/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientidentifier-resolver-system.xml</value>
+            <util:list>
+                <value>classpath:/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/providermetadata-resolver-system.xml
+                </value>
             </util:list>
         </property>
     </bean>
-    <util:list id="shibboleth.DefaultClientIdentifierResolverResources">
-        <value>%{idp.home}/conf/authn/oidc-client-registration-clientid.xml</value> <!-- should be a conditional:? -->
-    </util:list>
-    
-    <bean id="shibboleth.oidc.rp.OAuth2ClientIdentifierResolver"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.spring.impl.ReloadingOAuth2ClientIdentifierProvider"
-        c:resolverService-ref="shibboleth.oidc.rp.ClientIdentifierResolverService" />
-        
-   
+
+
+
+
 </beans>
\ No newline at end of file
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 84de395..df7f839 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
@@ -96,7 +96,6 @@
         
      <bean id="InitializeOAuth2ClientContext" scope="prototype"
         class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.InitializeOAuth2ClientContext"
-        p:clientIdResolver-ref="shibboleth.oidc.rp.OAuth2ClientIdentifierResolver"
         p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"/>
     
        
@@ -190,7 +189,6 @@
     
     <bean id="InitializeOAuth2ClientAuthenticationContext" scope="prototype"
         class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.InitializeOAuth2ClientAuthenticationContext"
-        p:clientAuthenticationResolver-ref="shibboleth.oidc.rp.OAuth2ClientAuthenticationResolver"
         p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"/>
         
     
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
index cb825fc..6e5cd49 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
@@ -29,8 +29,8 @@
         
         <!--  <evaluate expression="PostLookupPopulateAuditContext" /> -->
         <evaluate expression="InitializeOutboundMessageContext" />
-        <evaluate expression="InitializeOAuth2ClientContext" />
         <evaluate expression="SelectProfileConfiguration" />
+        <evaluate expression="InitializeOAuth2ClientContext" />        
         <evaluate expression="PopulateResponseTypeAndModeContext"/>
         <evaluate expression="AddOIDCAuthenticationRequest"/>
         <!-- <evaluate expression="PostRequestPopulateAuditContext" />
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
new file mode 100644
index 0000000..573f224
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
@@ -0,0 +1,93 @@
+<?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">
+
+    <!-- OIDC RP Profile Configurations. -->
+
+    <bean id="AbstractOIDCProfile" abstract="true"
+        p:securityConfiguration-ref="%{idp.security.authn.oidc.rp.config:shibboleth.oidc.DefaultSecurityConfiguration}" />
+
+    <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:refreshTokenLifetime="%{idp.authn.oidc.rp.refreshToken.defaultLifetime:PT2H}"
+        p:alwaysIncludedAttributes="%{idp.authn.oidc.rp.alwaysIncludedAttributes:}" />
+
+    <!-- FIXME This will NEED a new ID and possibly class. If not, the OP plugin and RP plugin can not be installed together -->
+    <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:deniedUserInfoAttributes="%{idp.authn.oidc.rp.deniedUserInfoAttributes:}"
+        p:clientIdLookupStrategy="#{getObject('shibboleth.authn.oidc.rp.ClientIdentifierLookupStrategy') ?: getObject('shibboleth.authn.oidc.rp.DefaultClientIdentifierLookupStrategy')}"
+        p:clientAuthenticationLookupStrategy="#{getObject('shibboleth.authn.oidc.rp.ClientAuthenticationLookupStrategy') ?: getObject('shibboleth.authn.oidc.rp.DefaultClientAuthenticationLookStrategy')}" />
+
+    
+    <bean id="shibboleth.authn.oidc.rp.DefaultClientAuthenticationLookStrategy"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.config.DefaultClientAuthenticationLookupStrategy"
+        c:clientId="%{idp.authn.oidc.rp.client.clientId:#{null}}"
+        c:clientSecret="%{idp.authn.oidc.rp.client.clientSecret:#{null}}"
+        c:clientAuthenticationMethod="%{idp.authn.oidc.rp.client.clientAuthenticationMethod:client_secret_basic}"
+        c:clientSecretExpiresAt="%{idp.authn.oidc.rp.client.clientSecretExpiresAt:PT0S}"
+        c:clientIdLookupStrategy="#{getObject('shibboleth.authn.oidc.rp.EmbeddedClientAuthenticationLookupStrategy') ?: getObject('shibboleth.authn.oidc.rp.StorageServiceBackedClientAuthenticationLookStrategy')}"/>
+    
+    
+    <bean id="shibboleth.authn.oidc.rp.DefaultClientIdentifierLookupStrategy" lazy-init="true"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.config.DefaultClientIdentifierLookupStrategy" 
+        c:clientId="%{idp.authn.oidc.rp.client.clientId:#{null}}"
+        c:clientIdLookupStrategy="#{getObject('shibboleth.authn.oidc.rp.EmbeddedClientIdentifierLookupStrategy') ?: getObject('shibboleth.authn.oidc.rp.StorageServiceBackedClientIdentifierLookupStrategy')}"/>
+    
+    <bean id="shibboleth.authn.oidc.rp.StorageServiceBackedClientIdentifierLookupStrategy" lazy-init="true"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.config.StorageServiceBackedClientIdentifierLookupStrategy"
+        p:providerMetadataLookupStrategy-ref="shibboleth.ChildLookup.OIDCProviderMetadataContextFromOutbound"
+        p:storageService="#{getObject('shibboleth.authn.oidc.rp.StorageService') ?: getObject('shibboleth.authn.oidc.rp.DefaultMapBackedClientIdStorageService')}" />
+    
+    <bean id="shibboleth.authn.oidc.rp.DefaultMapBackedClientIdStorageService" scope="prototype"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.config.MapBackedMemoryStorageServiceFactoryBean"
+        c:context="#{T(net.shibboleth.idp.plugin.authn.oidc.rp.config.StorageServiceBackedClientIdentifierLookupStrategy).CONTEXT_NAME}"
+        c:map="#{getObject('shibboleth.authn.oidc.rp.IssuerToClientIdMap')}"
+        c:id="DefaultIssuerToClientIDMapStorageService" />
+    
+    <bean id="shibboleth.authn.oidc.rp.StorageServiceBackedClientAuthenticationLookStrategy" lazy-init="true"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.config.StorageServiceBackedClientAuthenticationLookupStrategy"
+        p:storageService="#{getObject('shibboleth.authn.oidc.rp.ClientAuthenticationStorageService') ?: getObject('shibboleth.authn.oidc.rp.DefaultMapBackedClientAuthenticationStorageService')}" />
+
+    <bean id="shibboleth.authn.oidc.rp.DefaultMapBackedClientAuthenticationStorageService" lazy-init="true"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.config.MapBackedMemoryStorageServiceFactoryBean"
+        c:context="#{T(net.shibboleth.idp.plugin.authn.oidc.rp.config.StorageServiceBackedClientAuthenticationLookupStrategy).CONTEXT_NAME}"
+        c:map="#{getObject('shibboleth.authn.oidc.rp.ClientIdToClientAuthenticationMap')}"
+        c:id="DefaultClientIDToClientAuthenticationMapStorageService" />
+
+    <!-- Client Authentication parent bean which defaults secrets to not expire -->
+    <bean id="shibboleth.authn.oidc.rp.ClientAuthenticationDetails"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.storage.ClientAuthenticationDetails" abstract="true"
+        c:clientSecretExpiresAt="0" />
+
+    <!-- Security Configuration Defaults. These settings establish the default security configurations for signatures and 
+        loads the default credentials used. -->
+
+    <bean id="shibboleth.oidc.DefaultSecurityConfiguration"
+        class="net.shibboleth.oidc.profile.config.OIDCSecurityConfiguration">
+        <!-- Add these back were appropriate -->
+        <!-- <property name="signatureSigningConfiguration"> <ref bean="#{'%{idp.oidc.signing.config:shibboleth.oidc.SigningConfiguration}'.trim()}" 
+            /> </property> <property name="encryptionConfiguration"> <ref bean="#{'%{idp.oidc.encryption.config:shibboleth.oidc.EncryptionConfiguration}'.trim()}" 
+            /> </property> <property name="requestObjectDecryptionConfiguration"> <ref bean="#{'%{idp.oidc.rodecrypt.config:shibboleth.oidc.requestObjectDecryptionConfiguration}'.trim()}" 
+            /> </property> <property name="requestObjectSignatureValidationConfiguration"> <ref bean="#{'%{idp.oidc.rovalid.config:shibboleth.oidc.requestObjectSignatureValidationConfiguration}'.trim()}" 
+            /> </property> <property name="tokenEndpointJwtSignatureValidationConfiguration"> <ref bean="#{'%{idp.oidc.rovalid.config:shibboleth.oidc.tokenEndpointJwtSignatureValidationConfiguration}'.trim()}" 
+            /> </property> -->
+    </bean>
+
+
+
+</beans>
diff --git a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientauthentication-resolver-system.xml b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientauthentication-resolver-system.xml
deleted file mode 100644
index db1fc48..0000000
--- a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientauthentication-resolver-system.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<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" default-lazy-init="true">
-
-    <bean id="shibboleth.oidc.rp.OAuth2ClientAuthenticationProvider" lazy-init="false"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.spring.impl.OAuth2ClientAuthenticationContainer"
-        p:embeddedResolver-ref="shibboleth.oidc.rp.OAuth2ClientAuthenticationResolverImpl">
-    </bean>
-
-
-    <bean id="shibboleth.oidc.rp.OAuth2ClientAuthenticationResolverImpl"
-        class="net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolverImpl"
-        p:storageService="#{getObject('shibboleth.authn.oidc.rp.StorageService') ?: getObject('DefaultMapBackedClientAuthenticationStorageService')}" />
-
-    
-    <bean id="DefaultMapBackedClientAuthenticationStorageService" scope="prototype"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.MapBackedMemoryStorageServiceFactoryBean"
-        c:context="#{T(net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolverImpl).CONTEXT_NAME}"
-        c:map="#{getObject('shibboleth.authn.oidc.rp.ClientIdToClientAuthenticationMap')}"
-        c:id="DefaultClientIDToClientAuthenticationMapStorageService" />
-    
-    <!-- Client Authentication parent bean which defaults secrets to not expire -->
-    <bean id="shibboleth.authn.oidc.rp.ClientAuthenticationDetails"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.storage.ClientAuthenticationDetails" abstract="true" 
-        c:clientSecretExpiresAt="0"/>
-
-</beans>
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientidentifier-resolver-system.xml b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientidentifier-resolver-system.xml
deleted file mode 100644
index ac5f449..0000000
--- a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientidentifier-resolver-system.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<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" default-lazy-init="true">
-
-    <bean id="shibboleth.oidc.rp.OAuth2ClientIdentifierProvider" lazy-init="false"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.spring.impl.OAuth2ClientIdentifierContainer"
-        p:embeddedResolver-ref="shibboleth.oidc.rp.OAuth2ClientIdentifierResolverImpl">
-    </bean>
-
-    <bean id="shibboleth.oidc.rp.OAuth2ClientIdentifierResolverImpl"
-        class="net.shibboleth.oidc.security.impl.OAuth2ClientIdentifierResolverImpl"
-        p:providerMetadataLookupStrategy-ref="shibboleth.ChildLookup.OIDCProviderMetadataContextFromOutbound"
-        p:storageService="#{getObject('shibboleth.authn.oidc.rp.StorageService') ?: getObject('DefaultMapBackedClientIdStorageService')}" />
-
-
-    <bean id="DefaultMapBackedClientIdStorageService" scope="prototype"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.MapBackedMemoryStorageServiceFactoryBean"
-        c:context="#{T(net.shibboleth.oidc.security.impl.OAuth2ClientIdentifierResolverImpl).CONTEXT_NAME}"
-        c:map="#{getObject('shibboleth.authn.oidc.rp.IssuerToClientIdMap')}"
-        c:id="DefaultIssuerToClientIDMapStorageService" />
-
-
-
-</beans>
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/oidc-rp.properties b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/oidc-rp.properties
index 7e1b3ce..993518c 100644
--- a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/oidc-rp.properties
+++ b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/oidc-rp.properties
@@ -1,7 +1,21 @@
-idp.oidc.rp.clientID=client_id
-idp.oidc.rp.clientSecret=secret
-##does not need the .well-known/openid-configuration path.
-idp.oidc.rp.providerConfigurationDocument=https://hostname
-idp.oidc.rp.redirectURI=https://localhost:8443/idp/profile/Authn/OIDC/RP/callback
+## The downstream OP if discovery is not used
+idp.authn.oidc.rp.proxyIssuer= <issuerId>
+
+
+# If a redirect_uri is not explicitly declared above, one can be inferred from each
+# request's Host header. To avoid Host header injection attacks, the allowed origins
+# must be specified here. Origins are comma seperated. Do not specify the port when
+# using the default ports.
+idp.authn.oidc.rp.client.redirecturl.allowedOrigins = https://localhost
+
+
 ## openid is defaulted. Other scopes could be; profile etc.
-#idp.oidc.rp.scope=openid
\ No newline at end of file
+#idp.oidc.rp.scope=openid
+
+## A fixed client_id that can be used with the given proxy issuer.
+idp.authn.oidc.rp.client.clientId = client_id
+## A set of fixed client authentication parameters, which can be used when a single client is configured. If the secret
+## is set here, these settings will be enabled for client_authentication.
+idp.authn.oidc.rp.client.clientSecret = client_secret
+#idp.authn.oidc.rp.client.clientSecretExpiresAt = PT0S
+#idp.authn.oidc.rp.client.clientAuthenticationMethod = client_secret_basic
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientAuthenticationLookupStrategyTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientAuthenticationLookupStrategyTest.java
new file mode 100644
index 0000000..60d257e
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientAuthenticationLookupStrategyTest.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.config;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.time.Duration;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.testng.annotations.Test;
+
+import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
+import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
+import com.nimbusds.oauth2.sdk.auth.ClientSecretPost;
+import com.nimbusds.oauth2.sdk.auth.Secret;
+import com.nimbusds.oauth2.sdk.id.ClientID;
+
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+/** Tests for the DefaultClientAuthenticationLookupStrategy.*/
+public class DefaultClientAuthenticationLookupStrategyTest {
+    
+    /** Strategy to test.*/
+    private DefaultClientAuthenticationLookupStrategy strategy;
+    
+    @Test
+    public void testBuildFromProperties() throws ComponentInitializationException {
+        strategy = new DefaultClientAuthenticationLookupStrategy("client_id", "client_secret", "client_secret_basic", 
+                Duration.ofSeconds(0), s -> null);
+        strategy.setId("MockAuthenticationLookupStrategy");
+        strategy.initialize();
+        
+        final ClientAuthentication clientAuth = strategy.apply(new ProfileRequestContext());
+        assertEquals(clientAuth.getClientID().getValue(), "client_id");
+        assertTrue(clientAuth instanceof ClientSecretBasic);
+        assertEquals(((ClientSecretBasic)clientAuth).getClientSecret().getValue(), "client_secret");
+        assertFalse(((ClientSecretBasic)clientAuth).getClientSecret().expired());
+    }
+    
+    @Test
+    public void testBuildFromProperties_SecretExpired() throws ComponentInitializationException {
+        strategy = new DefaultClientAuthenticationLookupStrategy("client_id", "client_secret", "client_secret_basic", 
+                Duration.ofSeconds(1), s -> null);
+        strategy.setId("MockAuthenticationLookupStrategy");
+        strategy.initialize();
+        
+        final ClientAuthentication clientAuth = strategy.apply(new ProfileRequestContext());
+        assertNull(clientAuth);       
+    }
+    
+    @Test
+    public void testBuildFromStrategy() throws ComponentInitializationException {
+        strategy = new DefaultClientAuthenticationLookupStrategy(null, null, null, 
+                null, s -> new ClientSecretPost(new ClientID("client_id"), new Secret("client_secret")));
+        strategy.setId("MockAuthenticationLookupStrategy");
+        strategy.initialize();
+        
+        final ClientAuthentication clientAuth = strategy.apply(new ProfileRequestContext());
+        assertEquals(clientAuth.getClientID().getValue(), "client_id");
+        assertTrue(clientAuth instanceof ClientSecretPost);
+        assertEquals(((ClientSecretPost)clientAuth).getClientSecret().getValue(), "client_secret");
+        assertFalse(((ClientSecretPost)clientAuth).getClientSecret().expired());      
+    }
+    
+    @Test
+    public void testBuildFromStrategy_PartialClientProperties() throws ComponentInitializationException {
+        strategy = new DefaultClientAuthenticationLookupStrategy("client_id", "client_secret", null, 
+                null, s -> new ClientSecretPost(new ClientID("client_id"), new Secret("client_secret")));
+        strategy.setId("MockAuthenticationLookupStrategy");
+        strategy.initialize();
+        
+        final ClientAuthentication clientAuth = strategy.apply(new ProfileRequestContext());
+        assertEquals(clientAuth.getClientID().getValue(), "client_id");
+        assertTrue(clientAuth instanceof ClientSecretPost);
+        assertEquals(((ClientSecretPost)clientAuth).getClientSecret().getValue(), "client_secret");
+        assertFalse(((ClientSecretPost)clientAuth).getClientSecret().expired());      
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientAuthenticationLookupStrategyTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientAuthenticationLookupStrategyTest.java
new file mode 100644
index 0000000..6e0336b
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientAuthenticationLookupStrategyTest.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.config;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.storage.impl.MemoryStorageService;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
+
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.storage.ClientAuthenticationDetails;
+
+/** Tests for the StorageServiceBackedClientAuthenticationLookupStrategy.*/
+public class StorageServiceBackedClientAuthenticationLookupStrategyTest {
+    
+    /** The profile request context.*/
+    private ProfileRequestContext prc;
+    
+    /** Setup.*/
+    @BeforeMethod
+    public void setup() {
+        
+        prc = new ProfileRequestContext();
+        final var oauth2Context = new OAuth2ClientContext();
+        oauth2Context.setClientId("client_id");
+        final var outboundMsg = new MessageContext();
+        outboundMsg.getSubcontext(OIDCPeerEntityContext.class, true).addSubcontext(oauth2Context);
+        prc.setOutboundMessageContext(outboundMsg);     
+    }
+    
+    /**
+     * Test for a successful client authentication lookup.
+     * 
+     * @throws Exception on error
+     */
+    @Test
+    public void testLookupSuccess() throws Exception {
+        
+         
+        final var memStore = new MemoryStorageService();
+        memStore.setId("MockMemoryStorageService");
+        memStore.initialize();
+        
+        final var mapper = new ObjectMapper();
+        final var record = 
+                new ClientAuthenticationDetails("secret".toCharArray(), 0L, "client_secret_basic");
+              
+        memStore.create(
+                StorageServiceBackedClientAuthenticationLookupStrategy.CONTEXT_NAME, "client_id",  
+                mapper.writer().writeValueAsString(record), null);
+        
+        final var strategy = new StorageServiceBackedClientAuthenticationLookupStrategy();
+        strategy.setId("MockStrategy");
+        strategy.setStorageService(memStore);
+        strategy.initialize();
+        
+        final var clientAuth = strategy.apply(prc);
+        assertNotNull(clientAuth);
+        assertTrue(clientAuth instanceof ClientSecretBasic);
+        assertEquals(((ClientSecretBasic)clientAuth).getClientID().getValue(), "client_id");
+        assertEquals(((ClientSecretBasic)clientAuth).getClientSecret().getValue(), "secret");
+    }
+    
+    /**
+     * Test for an unsuccessful client authentication lookup, record does not exist.
+     * 
+     * @throws Exception on error
+     */
+    @Test
+    public void testLookupNoRecord() throws Exception {
+
+        final var memStore = new MemoryStorageService();
+        memStore.setId("MockMemoryStorageService");
+        memStore.initialize();
+        
+        final var strategy = new StorageServiceBackedClientAuthenticationLookupStrategy();
+        strategy.setId("MockStrategy");
+        strategy.setStorageService(memStore);
+        strategy.initialize();
+        
+        final var clientAuth = strategy.apply(prc);
+        assertNull(clientAuth);
+
+    }
+    
+    
+    /**
+     * Test for an unsuccessful client authentication lookup, wrong record exists.
+     * 
+     * @throws Exception on error
+     */
+    @Test
+    public void testLookupWrongRecord() throws Exception {
+                 
+        final var memStore = new MemoryStorageService();
+        memStore.setId("MockMemoryStorageService");
+        memStore.initialize();
+        
+        final var mapper = new ObjectMapper();
+        final var record = 
+                new ClientAuthenticationDetails("secret".toCharArray(), 0L, "client_secret_basic");
+              
+        memStore.create(
+                StorageServiceBackedClientAuthenticationLookupStrategy.CONTEXT_NAME, "different_client",  
+                mapper.writer().writeValueAsString(record), null);
+        
+        final var strategy = new StorageServiceBackedClientAuthenticationLookupStrategy();
+        strategy.setId("MockStrategy");
+        strategy.setStorageService(memStore);
+        strategy.initialize();
+        
+        final var clientAuth = strategy.apply(prc);
+        assertNull(clientAuth);
+        
+    }
+    
+    /**
+     * Test for an unsuccessful client authentication lookup, secret has expired.
+     * 
+     * @throws Exception on error
+     */
+    @Test
+    public void testLookupSecretExpired() throws Exception {
+              
+        final var memStore = new MemoryStorageService();
+        memStore.setId("MockMemoryStorageService");
+        memStore.initialize();
+        
+        final var mapper = new ObjectMapper();
+        final var record = 
+                new ClientAuthenticationDetails("secret".toCharArray(), 1L, "client_secret_basic");
+              
+        memStore.create(
+                StorageServiceBackedClientAuthenticationLookupStrategy.CONTEXT_NAME, "client_id",  
+                mapper.writer().writeValueAsString(record), null);
+        
+        final var strategy = new StorageServiceBackedClientAuthenticationLookupStrategy();
+        strategy.setId("MockStrategy");
+        strategy.setStorageService(memStore);
+        strategy.initialize();
+        
+        final var clientAuth = strategy.apply(prc);
+        assertNull(clientAuth);
+       
+    }
+    
+    /**
+     * Test for an unsuccessful client authentication lookup, incompatible record. Will log stack and return
+     * null.
+     * 
+     * @throws Exception on error
+     */
+    @Test
+    public void testLookupIncompatibleRecord() throws Exception {
+              
+        final var memStore = new MemoryStorageService();
+        memStore.setId("MockMemoryStorageService");
+        memStore.initialize();
+        
+        final var record = "{\"key\":\"value\"}";
+              
+        memStore.create(
+                StorageServiceBackedClientAuthenticationLookupStrategy.CONTEXT_NAME, "client_id",  
+                record, null);
+        
+        final var strategy = new StorageServiceBackedClientAuthenticationLookupStrategy();
+        strategy.setId("MockStrategy");
+        strategy.setStorageService(memStore);
+        strategy.initialize();
+        
+        final var clientAuth = strategy.apply(prc);
+        assertNull(clientAuth);
+       
+    }
+    
+
+}
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 1806afd..e49611f 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
@@ -70,6 +70,8 @@ import com.nimbusds.jwt.SignedJWT;
 import com.nimbusds.oauth2.sdk.ParseException;
 import com.nimbusds.oauth2.sdk.ResponseMode;
 import com.nimbusds.oauth2.sdk.ResponseType;
+import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
+import com.nimbusds.oauth2.sdk.auth.Secret;
 import com.nimbusds.oauth2.sdk.id.ClientID;
 import com.nimbusds.oauth2.sdk.id.State;
 import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
@@ -82,6 +84,7 @@ 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.config.StorageServiceBackedClientAuthenticationLookupStrategy;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.ResponseTypeAndModeContext;
@@ -106,6 +109,8 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
     private static final String OP_ISSUER_ID = "https://localhost:9918";
     
     private final String RP_ALLOWED_ORIGINS = "https://localhost";
+    
+    private static final String CLIENT_ID = "demo_rp";
 
     /**
      * Example of good provider metadata. Endpoints are localhost to support the 
@@ -257,22 +262,23 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         } catch (final Exception e) {
             log.error("Could not mock HTTP response",e);
         }
-
-        
+  
         loadBeanDefinitionsFromXmlFile(builderContext, 
-                new ClassPathResource("META-INF/net.shibboleth.idp/postconfig.xml"));
+                new ClassPathResource("META-INF/net.shibboleth.idp/postconfig.xml"),
+                null);
         
         loadBeanDefinitionsFromXmlFile(builderContext, 
-                new ClassPathResource("conf/test-relyingparty-resolver-service.xml"));
+                new ClassPathResource("conf/test-relyingparty-resolver-service.xml"), 
+                Map.of("idp.authn.oidc.rp.client.clientId", CLIENT_ID));
         
         loadBeanDefinitionsFromXmlFile(builderContext, 
-                new ClassPathResource("conf/additional-system-beans.xml"));
+                new ClassPathResource("conf/additional-system-beans.xml"), null);
         
         loadBeanDefinitionsFromXmlFile(builderContext, 
-                new ClassPathResource("attribute/registry/postconfig.xml"));
+                new ClassPathResource("attribute/registry/postconfig.xml"), null);
         
         loadBeanDefinitionsFromXmlFile(builderContext, 
-                new ClassPathResource("attribute/filter/attribute-filter-system.xml"));
+                new ClassPathResource("attribute/filter/attribute-filter-system.xml"), null);
     }
     
     /**
@@ -291,10 +297,10 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
                 .build();
         final var payload = new JWTClaimsSet.Builder()
                 .issuer(OP_ISSUER_ID)
-                .audience(List.of("demo_rp","demo_rp2"))
+                .audience(List.of(CLIENT_ID,"demo_rp2"))
                 .subject("jdoe")
                 .claim("nonce", "abadnonce")
-                .claim("azp", "demo_rp")
+                .claim("azp", CLIENT_ID)
                 .claim("name","jdoe")
                 .expirationTime(Date.from(Instant.now().plusSeconds(120)))
                 .build();
@@ -561,10 +567,15 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         final RelyingPartyContext partyContext = new RelyingPartyContext();
         final OIDCAuthorizationConfiguration partyConfig = new OIDCAuthorizationConfiguration();        
         partyContext.setProfileConfig(partyConfig);
+        partyConfig.setClientAuthenticationLookupStrategy(p ->
+            new ClientSecretBasic(new ClientID(CLIENT_ID), new Secret("secret")));
+                
         final RelyingPartyConfiguration rPartyConfig = new RelyingPartyConfiguration();
         rPartyConfig.setResponderId("http://idp.example.com/");
         partyContext.setConfiguration(rPartyConfig);
         nestPrc.addSubcontext(partyContext);
+        
+        
        
         // Setup outbound context
         final MessageContext outMsgCtx = new MessageContext();        
@@ -572,7 +583,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         outMsgCtx.addSubcontext(createPeerContext());
         outMsgCtx.addSubcontext(createResponseTypeAndModeContext());
         nestPrc.setOutboundMessageContext(outMsgCtx);    
-        outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext("demo_rp",null));
+        outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext(CLIENT_ID,null));
         
         // Setup inbound context.
         final MessageContext inMsgCtx = new MessageContext();
@@ -625,7 +636,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         // Second is userInfo
         mockOPServer.enqueue(new MockResponse().setResponseCode(200)
                 .setHeader("content-type", "application/jwt")
-                .setBody(createSignedUserInfoJWTResponseJSON(OP_ISSUER_ID,"demo_rp").serialize()));
+                .setBody(createSignedUserInfoJWTResponseJSON(OP_ISSUER_ID,CLIENT_ID).serialize()));
         mockOPServer.start(9918);
         
 
@@ -642,7 +653,15 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         final RelyingPartyContext partyContext = new RelyingPartyContext();
         final OIDCAuthorizationConfiguration partyConfig = new OIDCAuthorizationConfiguration();        
         partyContext.setProfileConfig(partyConfig);
+        partyConfig.setClientAuthenticationLookupStrategy(p ->
+            new ClientSecretBasic(new ClientID(CLIENT_ID), new Secret("secret")));
+                
+        final RelyingPartyConfiguration rPartyConfig = new RelyingPartyConfiguration();
+        rPartyConfig.setResponderId("http://idp.example.com/");
+        partyContext.setConfiguration(rPartyConfig);
         nestPrc.addSubcontext(partyContext);
+        
+       
        
         // Setup outbound context
         final MessageContext outMsgCtx = new MessageContext();        
@@ -650,7 +669,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         outMsgCtx.addSubcontext(createPeerContext());
         outMsgCtx.addSubcontext(createResponseTypeAndModeContext());
         nestPrc.setOutboundMessageContext(outMsgCtx);  
-        outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext("demo_rp",null));
+        outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext(CLIENT_ID,null));
         
         // Setup inbound context.
         final MessageContext inMsgCtx = new MessageContext();
@@ -704,7 +723,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         // Second is userInfo
         mockOPServer.enqueue(new MockResponse().setResponseCode(200)
                 .setHeader("content-type", "application/jwt")
-                .setBody(createSignedAndEncryptedUserInfoJWTResponseJSON(OP_ISSUER_ID,"demo_rp")
+                .setBody(createSignedAndEncryptedUserInfoJWTResponseJSON(OP_ISSUER_ID,CLIENT_ID)
                         .serialize()));
         mockOPServer.start(9918);
         
@@ -722,7 +741,14 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         final RelyingPartyContext partyContext = new RelyingPartyContext();
         final OIDCAuthorizationConfiguration partyConfig = new OIDCAuthorizationConfiguration();        
         partyContext.setProfileConfig(partyConfig);
+        partyConfig.setClientAuthenticationLookupStrategy(p ->
+            new ClientSecretBasic(new ClientID(CLIENT_ID), new Secret("secret")));
+                
+        final RelyingPartyConfiguration rPartyConfig = new RelyingPartyConfiguration();
+        rPartyConfig.setResponderId("http://idp.example.com/");
+        partyContext.setConfiguration(rPartyConfig);
         nestPrc.addSubcontext(partyContext);
+        
        
         // Setup outbound context
         final MessageContext outMsgCtx = new MessageContext();        
@@ -730,7 +756,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         outMsgCtx.addSubcontext(createPeerContext());
         outMsgCtx.addSubcontext(createResponseTypeAndModeContext());
         nestPrc.setOutboundMessageContext(outMsgCtx);    
-        outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext("demo_rp",null));
+        outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext(CLIENT_ID,null));
         
         // Setup inbound context.
         final MessageContext inMsgCtx = new MessageContext();
@@ -785,7 +811,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         // Second is userInfo
         mockOPServer.enqueue(new MockResponse().setResponseCode(200)
                 .setHeader("content-type", "application/jwt")
-                .setBody(createPlainUserInfoJWTResponseJSON(OP_ISSUER_ID,"demo_rp")
+                .setBody(createPlainUserInfoJWTResponseJSON(OP_ISSUER_ID,CLIENT_ID)
                         .serialize()));
         mockOPServer.start(9918);
         
@@ -801,8 +827,11 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         
         // Add under nest PRC
         final RelyingPartyContext partyContext = new RelyingPartyContext();
-        final OIDCAuthorizationConfiguration partyConfig = new OIDCAuthorizationConfiguration();        
+        final OIDCAuthorizationConfiguration partyConfig = new OIDCAuthorizationConfiguration();  
+        partyConfig.setClientAuthenticationLookupStrategy(new StorageServiceBackedClientAuthenticationLookupStrategy());
         partyContext.setProfileConfig(partyConfig);
+        partyConfig.setClientAuthenticationLookupStrategy(p ->
+                new ClientSecretBasic(new ClientID(CLIENT_ID), new Secret("secret")));
         nestPrc.addSubcontext(partyContext);
        
         // Setup outbound context
@@ -811,7 +840,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         outMsgCtx.addSubcontext(createPeerContext());
         outMsgCtx.addSubcontext(createResponseTypeAndModeContext());
         nestPrc.setOutboundMessageContext(outMsgCtx);    
-        outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext("demo_rp",null));
+        outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext(CLIENT_ID,null));
         
         // Setup inbound context.
         final MessageContext inMsgCtx = new MessageContext();
diff --git a/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml b/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
index d1e28d8..8fbf822 100644
--- a/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
+++ b/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
@@ -45,6 +45,22 @@
     <util:list id="shibboleth.RelyingPartyOverrides">
     
     </util:list>
+   
+    
+        <!-- 
+    Map clients to appropriate client authentication - only supports client_secret_basic and client_secret_post
+    -->
+    
+    <util:map id="shibboleth.authn.oidc.rp.ClientIdToClientAuthenticationMap">
+        <entry key="mytestclient">
+            <bean parent="shibboleth.authn.oidc.rp.ClientAuthenticationDetails" c:clientSecret="mytestsecret"
+                c:tokenEndpointAuthMethod="client_secret_basic" />
+        </entry>
+        <entry key="demo_rp">
+            <bean parent="shibboleth.authn.oidc.rp.ClientAuthenticationDetails" c:clientSecret="mytestsecret"
+                c:tokenEndpointAuthMethod="client_secret_basic" />
+        </entry>
+    </util:map>
 
 
 </beans>
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/test/resources/conf/test-relyingparty-resolver-service.xml b/idp-oidc-rp-impl/src/test/resources/conf/test-relyingparty-resolver-service.xml
index 7f7e263..bbdd4c1 100644
--- a/idp-oidc-rp-impl/src/test/resources/conf/test-relyingparty-resolver-service.xml
+++ b/idp-oidc-rp-impl/src/test/resources/conf/test-relyingparty-resolver-service.xml
@@ -24,26 +24,28 @@
                 c:serviceableClaz="net.shibboleth.idp.relyingparty.impl.DefaultRelyingPartyConfigurationResolver" />
         </constructor-arg>
     </bean>
-    
+
     <!-- Auto-append system config file to resource set. -->
-    <bean id ="ExtendedRelyingPartyResolverResources" class="net.shibboleth.ext.spring.factory.CombiningListFactoryBean"
-          p:firstList-ref="#{'%{idp.service.relyingparty.resources:shibboleth.RelyingPartyResolverResources}'.trim()}" >
+    <bean id="ExtendedRelyingPartyResolverResources"
+        class="net.shibboleth.ext.spring.factory.CombiningListFactoryBean"
+        p:firstList-ref="#{'%{idp.service.relyingparty.resources:shibboleth.RelyingPartyResolverResources}'.trim()}">
         <property name="secondList">
-            <util:list >
+            <util:list>
                 <value>classpath:/conf/test-relying-party-system.xml</value>
             </util:list>
         </property>
     </bean>
-    
+
     <bean id="shibboleth.RelyingPartyConfigurationResolver"
         class="net.shibboleth.idp.relyingparty.impl.ReloadingRelyingPartyConfigurationResolver"
         c:resolverService-ref="shibboleth.RelyingPartyResolverService" />
-        
+
     <util:list id="shibboleth.RelyingPartyResolverResources">
-     <!--    <value>%{idp.home}/conf/relying-party.xml</value>
-        <value>%{idp.home}/conf/credentials.xml</value> -->
+        <!-- <value>%{idp.home}/conf/relying-party.xml</value> <value>%{idp.home}/conf/credentials.xml</value> -->
     </util:list>
-    
-    
+
+   <!-- Wildcard import hook for plugins. -->
+    <import resource="classpath*:/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml" />
+
 
 </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