[java-oidc-common] 01/06: JCOMOIDC-48 - Move OIDC.SSO profile bean to commons

Phil Smart philip.smart at jisc.ac.uk
Thu Feb 2 14:50:06 UTC 2023


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

philsmart pushed a commit to branch dev/JCOMOIDC-48
in repository java-oidc-common.

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

commit e7d759c691bcce887a658eb1531bd408584c26d1
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Tue Jan 31 09:57:54 2023 +0000

    JCOMOIDC-48 - Move OIDC.SSO profile bean to commons
    
     - Move profile beans, related classes, and related config to commons
    
    https://shibboleth.atlassian.net/browse/JCOMOIDC-48
---
 .../oidc/common/conf/authn/oidc-common.properties  |  15 +
 .../oidc/common/conf/authn/oidc-credentials.xml    |  75 +++++
 .../net/shibboleth/oidc/common/module.properties   |   7 +
 .../config/AbstractOIDCSSOConfiguration.java       |  44 ++-
 .../profile/config/CredentialsListFactory.java     |  65 ++++
 .../config/OIDCAuthorizationConfiguration.java     | 148 +--------
 ...nticationContextClassRequestLookupFunction.java | 114 +++++++
 ...ticationContextClassResponseLookupFunction.java | 115 +++++++
 ...th2ClientAuthenticableProfileConfiguration.java | 131 +++++++-
 .../META-INF/net.shibboleth.idp/postconfig.xml     |  28 ++
 .../idp/service/relying-party/postconfig.xml       | 331 +++++++++++++++++++++
 .../profile/config/CredentialsListFactoryTest.java |  83 ++++++
 ...thenticationContextClassLookupFunctionTest.java | 134 +++++++++
 ...tionContextClassResponseLookupFunctionTest.java | 127 ++++++++
 14 files changed, 1268 insertions(+), 149 deletions(-)

diff --git a/oidc-common-plugin/src/main/resources/net/shibboleth/oidc/common/conf/authn/oidc-common.properties b/oidc-common-plugin/src/main/resources/net/shibboleth/oidc/common/conf/authn/oidc-common.properties
new file mode 100644
index 0000000..5a85c92
--- /dev/null
+++ b/oidc-common-plugin/src/main/resources/net/shibboleth/oidc/common/conf/authn/oidc-common.properties
@@ -0,0 +1,15 @@
+## Default, global, security configuration
+#idp.security.oidc.config = shibboleth.oidc.DefaultSecurityConfiguration
+## For the default security configuration. Will have no effect if the security configuration was overridden
+#idp.oidc.encryption.config = shibboleth.oidc.EncryptionConfiguration
+#idp.oidc.decryption.config = shibboleth.oidc.DecryptionConfiguration
+#idp.oidc.validation.config = shibboleth.oidc.SignatureValidationConfiguration
+#idp.oidc.signing.config = shibboleth.oidc.SigningConfiguration
+
+## How often keysets should be fetched from either a downstream relying party or upstream provider
+#idp.oidc.provider.keyfetch.interval = PT30M
+
+## Signing and encryption/decryption keys for request objects, id tokens, and userinfo response
+#idp.signing.oidc.rs.key = %{idp.home}/credentials/idp-signing-rs.jwk
+#idp.signing.oidc.es.key = %{idp.home}/credentials/idp-signing-es.jwk
+#idp.signing.oidc.rsa.enc.key = %{idp.home}/credentials/idp-encryption-rsa.jwk
\ No newline at end of file
diff --git a/oidc-common-plugin/src/main/resources/net/shibboleth/oidc/common/conf/authn/oidc-credentials.xml b/oidc-common-plugin/src/main/resources/net/shibboleth/oidc/common/conf/authn/oidc-credentials.xml
new file mode 100644
index 0000000..f38eb9a
--- /dev/null
+++ b/oidc-common-plugin/src/main/resources/net/shibboleth/oidc/common/conf/authn/oidc-credentials.xml
@@ -0,0 +1,75 @@
+<?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">
+
+    <!--
+    This file contains default OIDC signing credentials. This file should be imported into credentials.xml.
+    -->
+    
+    <bean id="shibboleth.oidc.DefaultRSSigningCredential" parent="shibboleth.oidc.JWKCredential"
+        p:resource="%{idp.signing.oidc.rs.key:#{null}}" p:failIfResourceIsNull="false" />
+
+    <bean id="shibboleth.oidc.DefaultESSigningCredential" parent="shibboleth.oidc.JWKCredential"
+        p:resource="%{idp.signing.oidc.es.key:#{null}}" p:failIfResourceIsNull="false" />
+
+    <bean id="shibboleth.oidc.DefaultRSAEncryptionCredential" parent="shibboleth.oidc.JWKCredential"
+        p:resource="%{idp.signing.oidc.rsa.enc.key:#{null}}" p:failIfResourceIsNull="false" />
+           
+    
+    <!-- 
+        The default credential is based on the client_secret, and will be the only used credential in most cases.
+    -->
+    <bean id="shibboleth.authn.oidc.rp.DefaultCredential" parent="shibboleth.oidc.ClientSecretCredential"
+        p:secret="%{idp.authn.oidc.rp.client.clientSecret:#{null}}"/>
+
+    <!--
+    Lists ALL of your OP's response signing credentials for the default security configuration. 
+    If you define additional signing credentials make sure to include them within this list.
+    -->
+    <util:list id="shibboleth.oidc.SigningCredentials">
+        <ref bean="shibboleth.oidc.DefaultRSSigningCredential" />
+        <ref bean="shibboleth.oidc.DefaultESSigningCredential" />
+    </util:list>
+
+    <!--
+    Lists ALL of your OP's request decryption credentials for the default security configuration.
+    If you  define additional decryption credentials make sure to include them within this list.
+    -->
+    <util:list id="shibboleth.oidc.EncryptionCredentials">
+        <ref bean="shibboleth.oidc.DefaultRSAEncryptionCredential" />
+    </util:list>
+
+    <!--
+    If you need to publish a key set different from shibboleth.oidc.EncryptionCredentials, define
+    a list bean named "shibboleth.oidc.EncryptionCredentialsToPublish".
+    -->
+
+    <!-- Example of two active encryption credentials, but only publishing the second before removing from use. -->
+    <!--
+    <util:list id="shibboleth.oidc.EncryptionCredentialsToPublish">
+        <ref bean="shibboleth.oidc.OnlyNewDefaultRSAEncryptionCredential" />
+    </util:list>
+    -->
+
+    <!--
+    If you need to publish a key set different from shibboleth.oidc.SigningCredentials, define
+    a list bean named "shibboleth.oidc.SigningCredentialsToPublish".
+    -->
+    
+    <!-- Example of publishing two signing credentials, before active use. -->
+    <!--
+    <util:list id="shibboleth.oidc.SigningCredentialsToPublish">
+        <ref bean="shibboleth.oidc.DefaultRSSigningCredential" />
+        <ref bean="shibboleth.oidc.DefaultESSigningCredential" />
+        <ref bean="shibboleth.oidc.UpcomingDefaultRSSigningCredential" />
+    </util:list>
+    -->
+
+</beans>
diff --git a/oidc-common-plugin/src/main/resources/net/shibboleth/oidc/common/module.properties b/oidc-common-plugin/src/main/resources/net/shibboleth/oidc/common/module.properties
index bfea30c..7bf3696 100644
--- a/oidc-common-plugin/src/main/resources/net/shibboleth/oidc/common/module.properties
+++ b/oidc-common-plugin/src/main/resources/net/shibboleth/oidc/common/module.properties
@@ -9,3 +9,10 @@ idp.oidc.common.3.plugin = net.shibboleth.oidc.common
 idp.oidc.common.3.name = Module to provide common OIDC processing
 idp.oidc.common.3.desc = Module that provides common OIDC processing functions for other plugins to use
 idp.oidc.common.3.url = /OIDCCommon
+
+idp.oidc.common.3.1.src =  /net/shibboleth/oidc/common/conf/authn/oidc.properties
+idp.oidc.common.3.1.dest = conf/authn/oidc.properties
+
+idp.oidc.common.3.2.src =  /net/shibboleth/oidc/common/conf/authn/oidc-credentials.xml
+idp.oidc.common.3.2.dest = conf/oidc-credentials.xml
+
diff --git a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/AbstractOIDCSSOConfiguration.java b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/AbstractOIDCSSOConfiguration.java
index c38c42f..93615f8 100644
--- a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/AbstractOIDCSSOConfiguration.java
+++ b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/AbstractOIDCSSOConfiguration.java
@@ -96,7 +96,10 @@ public abstract class AbstractOIDCSSOConfiguration extends AbstractOAuth2FlowAwa
     /** Lookup function to supply strategy bi-function for manipulating access token claims set. */ 
     @Nonnull
     private Function<ProfileRequestContext,BiFunction<ProfileRequestContext,Map<String,Object>,Map<String,Object>>>
-        accessTokenClaimsSetManipulationStrategyLookupStrategy;
+        accessTokenClaimsSetManipulationStrategyLookupStrategy;    
+    
+    /** Lookup function to override the default response_mode for a given response_type.*/
+    @Nonnull private Function<ProfileRequestContext, String> responseModeLookupStrategy;
 
     /**
      * Creates a new configuration instance.
@@ -122,9 +125,12 @@ public abstract class AbstractOIDCSSOConfiguration extends AbstractOAuth2FlowAwa
 
         idTokenManipulationStrategyLookupStrategy = FunctionSupport.constant(null);
         accessTokenClaimsSetManipulationStrategyLookupStrategy = FunctionSupport.constant(null);
+        
+        responseModeLookupStrategy = FunctionSupport.constant(null);
     }
     
     /** {@inheritDoc} */
+    @Override
     @Nullable @NotEmpty public String getIssuer(@Nullable final ProfileRequestContext profileRequestContext) {
         return issuerLookupStrategy.apply(profileRequestContext);
     }
@@ -148,6 +154,7 @@ public abstract class AbstractOIDCSSOConfiguration extends AbstractOAuth2FlowAwa
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean isResolveAttributes(@Nullable final ProfileRequestContext profileRequestContext) {
         return resolveAttributesPredicate.test(profileRequestContext);
     }
@@ -590,5 +597,40 @@ public abstract class AbstractOIDCSSOConfiguration extends AbstractOAuth2FlowAwa
         accessTokenClaimsSetManipulationStrategyLookupStrategy = Constraint.isNotNull(strategy,
                 "Lookup strategy cannot be null");
     }
+    
+    /**
+     * Set the lookup strategy to determine the response_mode for authorization requests.
+     *
+     * @param strategy the strategy to use
+     * 
+     * @since 2.2.0
+     */
+    public void setResponseModeLookupStrategy(@Nonnull final Function<ProfileRequestContext, String> strategy) {
+        responseModeLookupStrategy = Constraint.isNotNull(strategy, "ResponseMode lookup strategy can not be null");
+    }
+
+    /**
+     * Get the response_mode to use for authorization requests.
+     *
+     * @param profileRequestContext the profile request context
+     *
+     * @return the response_mode
+     * 
+     * @since 2.2.0
+     */
+    public String getResponseMode(@Nullable final ProfileRequestContext profileRequestContext) {
+        return responseModeLookupStrategy.apply(profileRequestContext);
+    }
+
+    /**
+     * Set the response_mode to use for authorization requests.
+     *
+     * @param responseMode the response_mode to use
+     * 
+     * @since 2.2.0
+     */
+    public void setResponseMode(@Nonnull final String responseMode) {
+        responseModeLookupStrategy = FunctionSupport.constant(responseMode);
+    }
 
 }
\ No newline at end of file
diff --git a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/CredentialsListFactory.java b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/CredentialsListFactory.java
new file mode 100644
index 0000000..b081ac8
--- /dev/null
+++ b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/CredentialsListFactory.java
@@ -0,0 +1,65 @@
+/*
+ * 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.oidc.profile.config;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.security.credential.Credential;
+import org.springframework.beans.factory.config.AbstractFactoryBean;
+
+/** 
+ * A factory that returns a list of credentials which does not contain any {@literal null} elements.
+ * 
+ * <p>Primarily created to support signature credential injection into the {@link BasicSignatureSigningConfiguration}
+ * when the credential list may contain, in its raw state, {@literal null} elements.</p>
+ */
+public class CredentialsListFactory extends AbstractFactoryBean<List<Credential>> {
+    
+    /** The credentials which may contain {@literal null} elements. */
+    @Nonnull private List<Credential> credentials;
+    
+    /**
+     * Constructor.
+     *
+     * @param creds the credentials, which can be {@literal null} and may contain {@literal null} elements.
+     */
+    public CredentialsListFactory(@Nullable final List<Credential> creds){
+        if (creds == null) {
+            credentials = Collections.emptyList();
+        } else {
+            credentials = creds;
+        }
+    }
+
+    @Override
+    public Class<?> getObjectType() {
+        return List.class;
+    }
+
+    @Override
+    protected List<Credential> createInstance() throws Exception {
+        return credentials.stream().filter(Objects::nonNull).collect(Collectors.toList());
+    }
+
+}
diff --git a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCAuthorizationConfiguration.java b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCAuthorizationConfiguration.java
index 6b1f6b0..6ab3c85 100644
--- a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCAuthorizationConfiguration.java
+++ b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCAuthorizationConfiguration.java
@@ -36,7 +36,6 @@ import com.google.common.base.Predicates;
 
 import net.shibboleth.idp.authn.config.AuthenticationProfileConfiguration;
 import net.shibboleth.idp.profile.config.OverriddenIssuerProfileConfiguration;
-import net.shibboleth.oidc.security.credential.ClientSecretCredential;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
@@ -102,22 +101,6 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
     /** An override to specify a specific redirect_uri to use over the normally computed one.*/
     @Nonnull private Function<ProfileRequestContext, String> redirectUriOverrideLookupStrategy;
 
-    /** Lookup function to retrieve the client_id from the given profile request context.*/
-    //TODO this is the same as the issuer in the proxy context?
-    @Nonnull private Function<ProfileRequestContext, String> clientIdLookupStrategy;
-
-    /**
-     * Lookup function to retrieve a client credential from the given profile request context.
-     */
-    @Nonnull private Function<ProfileRequestContext, ClientSecretCredential> clientCredentialLookupStrategy;
-
-    /** Lookup function to determine the client_authentication method.  */
-    //TODO same as tokenEndpointAuthMethodsLookupStrategy?
-    @Nonnull private Function<ProfileRequestContext, String> clientAuthenticationMethodLookupStrategy;
-
-    /** Lookup function to override the default response_mode for a given response_type.*/
-    @Nonnull private Function<ProfileRequestContext, String> responseModeLookupStrategy;
-
     /** Lookup function to retrieve the response_type.*/
     @Nonnull private Function<ProfileRequestContext, String> responseTypeLookupStrategy;
 
@@ -165,10 +148,7 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
     public OIDCAuthorizationConfiguration(@Nonnull @NotEmpty final String profileId) {
         super(profileId);
 
-        redirectUriOverrideLookupStrategy = FunctionSupport.constant(null);
-        clientIdLookupStrategy = FunctionSupport.constant(null);
-        clientCredentialLookupStrategy = FunctionSupport.constant(null);
-        responseModeLookupStrategy = FunctionSupport.constant(null);
+        redirectUriOverrideLookupStrategy = FunctionSupport.constant(null);     
         responseTypeLookupStrategy = FunctionSupport.constant("code");
         scopesLookupStrategy = FunctionSupport.constant(Set.of("openid"));
         signRequestObjectPredicate = Predicates.alwaysTrue();
@@ -369,103 +349,6 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
         encryptRequestObjectPredicate = Constraint.isNotNull(condition, "Condition cannot be null");
     }
 
-    /**
-     * Set the strategy to locate a client_id.
-     *
-     * @param strategy the strategy to use
-     */
-    public void setClientIdLookupStrategy(@Nonnull final Function<ProfileRequestContext, String> strategy) {
-        clientIdLookupStrategy = Constraint.isNotNull(strategy, "Client ID lookup strategy can not be null");
-    }
-
-    /**
-     * Set a fixed client_id to use no matter what the context/request.
-     *
-     * @param clientId the static clientId to use
-     */
-    public void setClientId(@Nullable final String clientId) {
-        clientIdLookupStrategy = FunctionSupport.constant(StringSupport.trimOrNull(clientId));
-    }
-
-    /**
-     * Get the client_id appropriate for the given context. Typically that associated
-     * with the chosen downstream OpenID Connect Provider.
-     *
-     * @param profileRequestContext the profile request context
-     *
-     * @return the client_id
-     */
-    public String getClientId(@Nullable final ProfileRequestContext profileRequestContext) {
-        return clientIdLookupStrategy.apply(profileRequestContext);
-    }
-
-     /**
-     * Set the client authentication method lookup strategy which will locate the client_authentication method
-     * for a given client.
-     *
-     * @param strategy the strategy to use
-     */
-    public void setClientAuthenticationMethodLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext, String> strategy) {
-        clientAuthenticationMethodLookupStrategy =
-                Constraint.isNotNull(strategy, "Client authentication strategy can not be null");
-    }
-
-    /**
-     * Set the client authentication method.
-     *
-     * @param clientAuthMethod the client_authentication method
-     */
-    public void setClientAuthenticationMethod(@Nullable final String clientAuthMethod) {
-        clientAuthenticationMethodLookupStrategy = FunctionSupport.constant(clientAuthMethod);
-    }
-
-     /**
-      * Get the client_authentication for the given context. Typically that associated with the
-      * current client_id.
-      *
-      * @param profileRequestContext the profile request context
-      *
-      * @return the client_authentication method
-      */
-     public String getClientAuthenticationMethod(
-             @Nullable final ProfileRequestContext profileRequestContext) {
-         return clientAuthenticationMethodLookupStrategy.apply(profileRequestContext);
-     }
-
-    /**
-     * Set the client credential lookup strategy.
-     *
-     * @param strategy the strategy to use
-     */
-    public void setClientCredentialLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext, ClientSecretCredential> strategy) {
-        clientCredentialLookupStrategy =
-                Constraint.isNotNull(strategy, "Client credential lookup strategy can not be null");
-    }
-
-    /**
-     * Set a fixed client credential to use no matter what the context/request.
-     *
-     * @param clientCredential the static client credential to use
-     */
-    public void setClientCredential(@Nullable final ClientSecretCredential clientCredential) {
-        clientCredentialLookupStrategy = FunctionSupport.constant(clientCredential);
-    }
-
-    /**
-     * Get the client credential for the given context. Typically a client_secret
-     * associated with the current client_id.
-     *
-     * @param profileRequestContext the profile request context
-     *
-     * @return the client credential
-     */
-    public ClientSecretCredential getClientCredential(
-            @Nullable final ProfileRequestContext profileRequestContext) {
-        return clientCredentialLookupStrategy.apply(profileRequestContext);
-    }
-
     /**
      * Set the redirect_uri lookup strategy used to locate an overridden redirect.
      *
@@ -672,35 +555,6 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
             @Nonnull final Function<ProfileRequestContext,Set<String>> strategy) {
         deniedUserInfoAttributesLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
     }
-
-    /**
-     * Set the lookup strategy to determine the response_mode for authentication requests.
-     *
-     * @param strategy the strategy to use
-     */
-    public void setResponseModeLookupStrategy(@Nonnull final Function<ProfileRequestContext, String> strategy) {
-        responseModeLookupStrategy = Constraint.isNotNull(strategy, "ResponseMode lookup strategy can not be null");
-    }
-
-    /**
-     * Get the response_mode to use for authentication requests.
-     *
-     * @param profileRequestContext the profile request context
-     *
-     * @return the response_mode
-     */
-    public String getResponseMode(@Nullable final ProfileRequestContext profileRequestContext) {
-        return responseModeLookupStrategy.apply(profileRequestContext);
-    }
-
-    /**
-     * Set the response_mode to use for authentication requests.
-     *
-     * @param responseMode the response_mode to use
-     */
-    public void setResponseMode(@Nonnull final String responseMode) {
-        responseModeLookupStrategy = FunctionSupport.constant(responseMode);
-    }
     
     /**
      * Get whether to include iss parameter in the authentication response.
diff --git a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/navigate/ProxyAwareDefaultOIDCAuthenticationContextClassRequestLookupFunction.java b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/navigate/ProxyAwareDefaultOIDCAuthenticationContextClassRequestLookupFunction.java
new file mode 100644
index 0000000..ffded26
--- /dev/null
+++ b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/navigate/ProxyAwareDefaultOIDCAuthenticationContextClassRequestLookupFunction.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.shibboleth.oidc.profile.config.navigate;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.RequestedPrincipalContext;
+import net.shibboleth.oidc.authn.principal.AuthenticationContextClassReferencePrincipal;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+
+/**
+ * Implements a set of default logic for determining the custom principals to derive the
+ * OIDC ACRs from.
+ * 
+ * <p>This operates for the SAML to OIDC proxy use case. The values returned are empty unless the 
+ * parent context itself contains a child context carrying particular values. The values are either
+ * 'passed through' or mapped by the given principal mappings. All input values are either SAML ACRs or AMRs,
+ * and all output values are OIDC ACRs.</p>
+ */
+public class ProxyAwareDefaultOIDCAuthenticationContextClassRequestLookupFunction 
+    implements Function<ProfileRequestContext,Collection<AuthenticationContextClassReferencePrincipal>> {
+    
+    /** Mappings to transform proxied Principals. */
+    @Nonnull @NonnullElements private Map<Principal,Collection<Principal>> principalMappings;
+    
+    /** Constructor. */
+    public ProxyAwareDefaultOIDCAuthenticationContextClassRequestLookupFunction() {
+        principalMappings = Collections.emptyMap();
+    }
+    
+   /**
+    * Sets the mappings from input/proxied Principals to zero or more equivalent values to use.
+    * 
+    * <p>Any values not mapped will be assumed to be passed through.</p>
+    * 
+    * @param mappings {@link Principal} mappings
+    */
+   public void setMappings(@Nullable @NonnullElements final Map<Principal,Collection<Principal>> mappings) {
+       if (mappings == null || mappings.isEmpty()) {
+           principalMappings = Collections.emptyMap();
+           return;
+       }
+       
+       principalMappings = new HashMap<>(mappings.size());
+       mappings.forEach((k, v) -> principalMappings.put(k, List.copyOf(v)));
+   }
+
+    @Override
+    public Collection<AuthenticationContextClassReferencePrincipal> apply(final ProfileRequestContext input) {
+        
+        if (input != null && input.getParent() instanceof AuthenticationContext) {
+            
+            final RequestedPrincipalContext rpc = input.getParent().getSubcontext(RequestedPrincipalContext.class);
+            
+            if (rpc != null) {
+                
+                final List<AuthenticationContextClassReferencePrincipal> mappedAndPassedThroughPrincipals =
+                        new ArrayList<>();
+                
+                for (final Principal requestedPrincipal : rpc.getRequestedPrincipals()) {
+                    
+                    // If a mapping exists, use it
+                    if (principalMappings.containsKey(requestedPrincipal)) {
+                        final Collection<Principal> mappedPrinicipals = principalMappings.get(requestedPrincipal);
+                        for (final Principal mappedPrincipal : mappedPrinicipals) {
+                            if (mappedPrincipal instanceof AuthenticationContextClassReferencePrincipal) {
+                                mappedAndPassedThroughPrincipals.add(
+                                        (AuthenticationContextClassReferencePrincipal) mappedPrincipal);
+                            }                               
+                        }
+                    } else {
+                        // If no mapping exists, just convert to correct output type - which might not make sense
+                        // to the upstream OP.
+                        mappedAndPassedThroughPrincipals.add(
+                                new AuthenticationContextClassReferencePrincipal(requestedPrincipal.getName()));
+                    }
+                
+                }
+                return mappedAndPassedThroughPrincipals;
+            }
+        }        
+        return Collections.emptyList();
+    }
+    
+
+}
+
diff --git a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/navigate/ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunction.java b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/navigate/ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunction.java
new file mode 100644
index 0000000..a122310
--- /dev/null
+++ b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/navigate/ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunction.java
@@ -0,0 +1,115 @@
+/*
+ * 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.oidc.profile.config.navigate;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+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.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Implements a set of logic for determining the principals to use from OIDC 'amr' or 'acr' claims.
+ * 
+ * <p>This operates for the OIDC to SAML proxy use case, in effect the reverse of the 
+ * {@link ProxyAwareDefaultOIDCAuthenticationContextClassRequestLookupFunction} function. 
+ * All input values are either OIDC ACRs or AMRs, and all output values Java {@link Principal}s.
+ * The values are mapped by the given principal mappings. If a mapping does not exist, the AMR or ACR is ignored.
+ * </p>
+ */
+ at ThreadSafe
+public class ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunction 
+    implements Function<Collection<String>,Collection<Principal>> {
+    
+    /** Mappings to transform proxied Principals. */
+    @Nonnull @NonnullElements private final Map<String,Collection<Principal>> principalMappings;
+    
+    /**
+     * 
+     * Constructor.
+     *
+     * @param mappings the AMR/ACR value to Principal mappings
+     */
+    public ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunction(
+            @Nullable @NonnullElements @ParameterName(name="mappings") 
+            final Map<String,Collection<Principal>> mappings) {
+        
+        if (mappings == null || mappings.isEmpty()) {
+            principalMappings = Collections.emptyMap();
+        } else {        
+            principalMappings = new HashMap<>(mappings.size());
+            mappings.forEach((k, v) -> principalMappings.put(k, List.copyOf(v)));
+        }
+    }
+    
+    @Override
+    public Collection<Principal> apply(final Collection<String> amrOrAcrs) {
+        
+        if (amrOrAcrs != null) {                
+                final List<Principal> principals = new ArrayList<>();                
+                for (final String amrOrAcr : amrOrAcrs) {                    
+                    if (principalMappings.containsKey(amrOrAcr)) {
+                        final Collection<Principal> mappedPrincipals = principalMappings.get(amrOrAcr);
+                        if (!mappedPrincipals.isEmpty()) {
+                            principals.addAll(mappedPrincipals);
+                        }
+                    }                 
+                }
+                return principals;            
+        }        
+        return Collections.emptyList();
+    }   
+    
+    /** A simple lookup function that returns a singleton function.*/
+    public static class LookupFunctionWrapper 
+                implements Function<ProfileRequestContext, Function<Collection<String>,Collection<Principal>>> {
+
+        /** A function used to map OIDC ACR/AMRs to Principals. A single instance is supplied.*/
+        private final Function<Collection<String>,Collection<Principal>> function; 
+        
+        /**
+         * 
+         * Constructor.
+         *
+         * @param wrappedFunction the function to return when requested.          
+         */
+        public LookupFunctionWrapper(@ParameterName(name="wrappedFunction") 
+                final Function<Collection<String>,Collection<Principal>> wrappedFunction) {
+            function = Constraint.isNotNull(wrappedFunction, "Lookup function can not be null");
+        }
+        
+        @Override
+        public Function<Collection<String>, Collection<Principal>> apply(final ProfileRequestContext prc) {
+            return function;
+        }
+        
+    }
+}
+
diff --git a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/oauth2/config/AbstractOAuth2ClientAuthenticableProfileConfiguration.java b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/oauth2/config/AbstractOAuth2ClientAuthenticableProfileConfiguration.java
index 86584f4..d6d0f9f 100644
--- a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/oauth2/config/AbstractOAuth2ClientAuthenticableProfileConfiguration.java
+++ b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/oauth2/config/AbstractOAuth2ClientAuthenticableProfileConfiguration.java
@@ -37,6 +37,7 @@ import net.shibboleth.idp.authn.config.AuthenticationProfileConfiguration;
 import net.shibboleth.idp.profile.config.AbstractConditionalProfileConfiguration;
 import net.shibboleth.oidc.authn.principal.AuthenticationContextClassReferencePrincipal;
 import net.shibboleth.oidc.jwt.claims.ClaimsValidator;
+import net.shibboleth.oidc.security.credential.ClientSecretCredential;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonNegative;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
@@ -56,6 +57,9 @@ public abstract class AbstractOAuth2ClientAuthenticableProfileConfiguration
     /** Enabled token endpoint authentication methods. */
     @Nonnull private Function<ProfileRequestContext,Set<String>> tokenEndpointAuthMethodsLookupStrategy;
     
+    /** The token endpoint authentication method to use with an upstream OpenID Provider. */
+    @Nonnull private Function<ProfileRequestContext,String> tokenEndpointAuthMethodLookupStrategy;
+    
     /** Validation of JWT claims for subset of client auth methods. */
     @Nonnull private Function<ProfileRequestContext,ClaimsValidator> claimsValidatorLookupStrategy;
 
@@ -75,6 +79,14 @@ public abstract class AbstractOAuth2ClientAuthenticableProfileConfiguration
     /** Lookup function to supply post authentication flows. */
     @Nonnull private Function<ProfileRequestContext,Collection<String>> postAuthenticationFlowsLookupStrategy;
     
+    /** Lookup function to retrieve a client credential from the given profile request context. */
+    @Nonnull private Function<ProfileRequestContext, ClientSecretCredential> clientCredentialLookupStrategy;
+    
+    /** Lookup function to retrieve the client_id from the given profile request context.*/
+    //TODO this is the same as the issuer in the proxy context?
+    @Nonnull private Function<ProfileRequestContext, String> clientIdLookupStrategy;
+
+    
     /**
      * Constructor.
      *
@@ -89,13 +101,17 @@ public abstract class AbstractOAuth2ClientAuthenticableProfileConfiguration
                         ClientAuthenticationMethod.CLIENT_SECRET_POST.toString(),
                         ClientAuthenticationMethod.CLIENT_SECRET_JWT.toString(),
                         ClientAuthenticationMethod.PRIVATE_KEY_JWT.toString()));
+        tokenEndpointAuthMethodLookupStrategy = 
+                FunctionSupport.constant(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.toString());
         claimsValidatorLookupStrategy = FunctionSupport.constant(null);
-        
         forceAuthnPredicate = Predicates.alwaysFalse();
         proxyCountLookupStrategy = FunctionSupport.constant(null);
         defaultAuthenticationContextsLookupStrategy = FunctionSupport.constant(null);
         authenticationFlowsLookupStrategy = FunctionSupport.constant(null);
         postAuthenticationFlowsLookupStrategy = FunctionSupport.constant(null);
+        clientCredentialLookupStrategy = FunctionSupport.constant(null);
+        clientIdLookupStrategy = FunctionSupport.constant(null);   
+        
     }
 
     /**
@@ -141,6 +157,44 @@ public abstract class AbstractOAuth2ClientAuthenticableProfileConfiguration
         tokenEndpointAuthMethodsLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
     }
     
+    /**
+     * Get the token endpoint authentication method to use with an upstream OpenID Provider.
+     * 
+     * @param profileRequestContext profile request context
+     * 
+     * @return the token endpoint authentication method to use.
+     * 
+     * @since 2.2.0
+     */
+    @Nonnull public String getTokenEndpointAuthMethod(@Nullable final ProfileRequestContext profileRequestContext) {
+        return tokenEndpointAuthMethodLookupStrategy.apply(profileRequestContext);
+    }
+    
+    /**
+     * Set the enabled token endpoint authentication method to use with an upstream OpenID Provider.
+     * 
+     * @param method the token endpoint authentication method to set.
+     * 
+     * @since 2.2.0
+     */
+    public void setTokenEndpointAuthMethod(@Nonnull @NonnullElements final String method) {
+        tokenEndpointAuthMethodLookupStrategy =
+                FunctionSupport.constant(Constraint.isNotNull(method, "Collection of methods cannot be null"));
+    }
+
+    /**
+     * Set a lookup strategy to find the enabled token endpoint authentication method to use with an 
+     * upstream OpenID Provider.
+     *
+     * @param strategy  lookup strategy
+     * 
+     * @since 2.2.0
+     */
+    public void setTokenEndpointAuthMethodLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, String> strategy) {
+        tokenEndpointAuthMethodLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+    }
+    
     /**
      * Get the {@link ClaimsValidator} to apply to JWT-based client authentication.
      * 
@@ -343,4 +397,79 @@ public abstract class AbstractOAuth2ClientAuthenticableProfileConfiguration
         defaultAuthenticationContextsLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
     }
 
+    /**
+     * Set the client credential lookup strategy.
+     *
+     * @param strategy the strategy to use
+     * 
+     * @since 2.2.0
+     */
+    public void setClientCredentialLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, ClientSecretCredential> strategy) {
+        clientCredentialLookupStrategy =
+                Constraint.isNotNull(strategy, "Client credential lookup strategy can not be null");
+    }
+
+    /**
+     * Set a fixed client credential to use no matter what the context/request.
+     *
+     * @param clientCredential the static client credential to use
+     * 
+     * @since 2.2.0
+     */
+    public void setClientCredential(@Nullable final ClientSecretCredential clientCredential) {
+        clientCredentialLookupStrategy = FunctionSupport.constant(clientCredential);
+    }
+
+    /**
+     * Get the client credential for the given context. Typically a client_secret
+     * associated with the current client_id.
+     *
+     * @param profileRequestContext the profile request context
+     *
+     * @return the client credential
+     * 
+     * @since 2.2.0
+     */
+    public ClientSecretCredential getClientCredential(
+            @Nullable final ProfileRequestContext profileRequestContext) {
+        return clientCredentialLookupStrategy.apply(profileRequestContext);
+    }
+    
+    /**
+     * Set the strategy to locate a client_id.
+     *
+     * @param strategy the strategy to use
+     * 
+     * @since 2.2.0
+     */
+    public void setClientIdLookupStrategy(@Nonnull final Function<ProfileRequestContext, String> strategy) {
+        clientIdLookupStrategy = Constraint.isNotNull(strategy, "Client ID lookup strategy can not be null");
+    }
+
+    /**
+     * Set a fixed client_id to use no matter what the context/request.
+     *
+     * @param clientId the static clientId to use
+     * 
+     * @since 2.2.0
+     */
+    public void setClientId(@Nullable final String clientId) {
+        clientIdLookupStrategy = FunctionSupport.constant(StringSupport.trimOrNull(clientId));
+    }
+
+    /**
+     * Get the client_id appropriate for the given context. Typically that associated
+     * with the chosen downstream OpenID Connect Provider.
+     *
+     * @param profileRequestContext the profile request context
+     *
+     * @return the client_id
+     * 
+     * @since 2.2.0
+     */
+    public String getClientId(@Nullable final ProfileRequestContext profileRequestContext) {
+        return clientIdLookupStrategy.apply(profileRequestContext);
+    }
+    
 }
\ No newline at end of file
diff --git a/oidc-common-profile-api/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/oidc-common-profile-api/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
new file mode 100644
index 0000000..245a048
--- /dev/null
+++ b/oidc-common-profile-api/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:context="http://www.springframework.org/schema/context"
+    xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
+    xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+    default-init-method="initialize" default-destroy-method="destroy">
+
+    <!-- Generic OIDC System beans needed for OIDC based extensions to function, loaded after global.xml.  -->
+    
+    <!-- Credential bean factories -->
+    <bean id="shibboleth.oidc.ClientSecretCredential" abstract="true"
+        class="net.shibboleth.oidc.security.credential.impl.BasicClientSecretCredentialFactoryBean" />    
+        
+    <bean id="shibboleth.oidc.JWKCredential" abstract="true"
+        class="net.shibboleth.oidc.security.credential.impl.BasicJWKCredentialFactoryBean" />
+    
+    <!-- Singleton JWK cache -->
+    <bean id="shibboleth.oidc.RemoteJwkSetCache"
+        class="net.shibboleth.oidc.jwk.RemoteJwkSetCache"
+        p:storage-ref="#{'%{idp.oidc.jwk.StorageService:shibboleth.StorageService}'.trim()}"
+        p:httpClient="#{getObject('shibboleth.oidc.NonBrowser.HttpClient') ?: getObject('shibboleth.InternalHttpClient')}"
+        p:httpClientSecurityParameters="#{getObject('shibboleth.oidc.NonBrowser.HttpClientSecurityParameters')}" />
+
+</beans>
\ No newline at end of file
diff --git a/oidc-common-profile-api/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml b/oidc-common-profile-api/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
new file mode 100644
index 0000000..821e6ea
--- /dev/null
+++ b/oidc-common-profile-api/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
@@ -0,0 +1,331 @@
+<?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">
+        
+   <!-- 
+        Security Configuration Defaults. These settings establish the default security configurations for 
+        signatures and loads the default credentials used. 
+    --> 
+   <bean id="shibboleth.oidc.BasicSignatureValidationConfiguration" abstract="true"
+        class="net.shibboleth.oidc.security.jose.impl.BasicSignatureValidationConfiguration"
+        p:includedAlgorithms="#{getObject('shibboleth.oidc.IncludedSignatureAlgorithms')}"
+        p:excludedAlgorithms="#{getObject('shibboleth.oidc.ExcludedSignatureAlgorithms')}" />
+    
+    <bean id="shibboleth.oidc.BasicSignatureSigningConfiguration" abstract="true"
+        class="net.shibboleth.oidc.security.jose.impl.BasicSignatureSigningConfiguration"
+        p:includedAlgorithms="#{getObject('shibboleth.oidc.IncludedSignatureAlgorithms')}"
+        p:excludedAlgorithms="#{getObject('shibboleth.oidc.ExcludedSignatureAlgorithms')}" />
+    
+    <bean id="shibboleth.oidc.BasicEncryptionConfiguration" abstract="true"
+        class="net.shibboleth.oidc.security.jose.impl.BasicEncryptionConfiguration"
+        p:includedAlgorithms="#{getObject('shibboleth.oidc.IncludedEncryptionAlgorithms')}"
+        p:excludedAlgorithms="#{getObject('shibboleth.oidc.ExcludedEncryptionAlgorithms')}" />
+
+    <bean id="shibboleth.oidc.BasicDecryptionConfiguration" abstract="true"
+        class="net.shibboleth.oidc.security.jose.impl.BasicDecryptionConfiguration"
+        p:includedAlgorithms="#{getObject('shibboleth.oidc.IncludedEncryptionAlgorithms')}"
+        p:excludedAlgorithms="#{getObject('shibboleth.oidc.ExcludedEncryptionAlgorithms')}" />
+        
+    <bean id="AbstractOIDCProfile" abstract="true"
+        p:securityConfiguration-ref="%{idp.security.oidc.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_poster,client_secret_jwt,private_key_jwt}"
+        p:claimsValidator-ref="DefaultJWTClaimsValidator"
+        p:encryptionOptional="%{idp.oidc.encryptionOptional:true}"
+        p:forcePKCE="%{idp.oidc.forcePKCE:false}"
+        p:allowPKCEPlain="%{idp.oidc.allowPKCEPlain:false}"
+        p:iDTokenLifetime="%{idp.oidc.idToken.defaultLifetime:PT1H}"
+        p:accessTokenType="%{idp.oauth2.accessToken.type:}"
+        p:accessTokenLifetime="%{idp.oidc.accessToken.defaultLifetime:PT10M}"
+        p:refreshTokenLifetime="%{idp.oidc.refreshToken.defaultLifetime:PT2H}"
+        p:alwaysIncludedAttributes="%{idp.oidc.alwaysIncludedAttributes:}"
+        
+        p:tokenEndpointAuthMethod="%{idp.authn.oidc.rp.client.authenticationMethod:client_secret_basic}"
+        p:responseMode="%{idp.authn.oidc.rp.client.responseMode:#{null}}"
+        p:clientId="#{%{idp.authn.oidc.rp.provider.discoveryRequired:false} == true ? null : '%{idp.authn.oidc.rp.client.clientId:}'}"
+        p:clientCredential="#{%{idp.authn.oidc.rp.provider.discoveryRequired:false} == true ? {null} : getObject('shibboleth.authn.oidc.rp.DefaultCredential')}"/>
+
+   <bean id="OIDC.SSO" parent="AbstractOIDCSSOProfile" lazy-init="true"
+        class="net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration"
+        
+        p:authorizeCodeLifetime="%{idp.oidc.authorizeCode.defaultLifetime:PT5M}"
+        p:encodeConsentInTokens="%{idp.oidc.encodeConsentInTokens:false}"
+        p:encodedAttributes="%{idp.oidc.encodedAttributes:%{idp.oidc.embeddedAttributes:}}"
+        p:deniedUserInfoAttributes="%{idp.oidc.deniedUserInfoAttributes:}"
+        
+        p:httpRequestMethod="%{idp.authn.oidc.rp.httpRequestMethod:GET}"       
+        p:retrieveUserInfoEndpointClaims="%{idp.authn.oidc.rp.client.userinfo.enabled:true}"        
+        p:redirectUriOverride="%{idp.authn.oidc.rp.client.redirectURI:#{null}}"
+        p:encryptRequestObject="%{idp.authn.oidc.rp.client.requestobject.encrypted:false}"
+        p:signRequestObject="%{idp.authn.oidc.rp.client.requestobject.signed:true}"
+        p:useRequestObject="%{idp.authn.oidc.rp.client.requestobject.supported:false}"
+        p:scopes="%{idp.authn.oidc.rp.client.scopes:#{null}}">
+        <property name="forceAuthnPredicate">
+            <bean class="net.shibboleth.idp.saml.profile.config.logic.ProxyAwareForceAuthnPredicate" />
+        </property>
+        <property name="defaultAuthenticationMethodsLookupStrategy">
+            <bean
+                class="net.shibboleth.oidc.profile.config.navigate.ProxyAwareDefaultOIDCAuthenticationContextClassRequestLookupFunction"
+                p:mappings="#{getObject('shibboleth.oidc.PrincipalProxyRequestMappings')}" />
+        </property>
+         <property name="authenticationContextClassReferenceTranslationStrategyLookupStrategy">
+            <bean
+                class="net.shibboleth.oidc.profile.config.navigate.ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunction$LookupFunctionWrapper">
+                <constructor-arg>
+                    <bean class="net.shibboleth.oidc.profile.config.navigate.ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunction"
+                    c:mappings="#{getObject('shibboleth.oidc.PrincipalProxyResponseMappings')}"/>
+                </constructor-arg>
+            </bean>
+        </property>
+         <property name="authenticationMethodsReferencesTranslationStrategyLookupStrategy">
+            <bean
+                class="net.shibboleth.oidc.profile.config.navigate.ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunction$LookupFunctionWrapper">
+                <constructor-arg>
+                    <bean class="net.shibboleth.oidc.profile.config.navigate.ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunction"
+                    c:mappings="#{getObject('shibboleth.oidc.PrincipalProxyResponseMappings')}"/>
+                </constructor-arg>
+            </bean>
+        </property>
+    </bean>
+    
+    <!-- Keyset profile -->
+    <bean id="OIDC.Keyset" parent="AbstractOIDCProfile" lazy-init="true"
+        class="net.shibboleth.oidc.profile.config.OIDCPublishKeySetConfiguration"
+        p:securityConfiguration-ref="shibboleth.oidc.PublishKeySetSecurityConfiguration" />
+        
+     <!--  Configuration for publishing key set. -->
+    <bean id="shibboleth.oidc.PublishKeySetSecurityConfiguration"
+        class="net.shibboleth.oidc.profile.config.JSONSecurityConfiguration">
+       <property name="jwtSignatureSigningConfiguration">
+            <ref bean="shibboleth.oidc.PublishKeySetSigningConfiguration" />
+        </property>
+        <property name="jwtDecryptionConfiguration">
+            <ref bean="shibboleth.oidc.PublishKeySetDecryptionConfiguration" />
+        </property>
+    </bean>
+    
+    <!--  Signing keys to publish. -->
+    <bean id="shibboleth.oidc.PublishKeySetSigningConfiguration" 
+        parent="shibboleth.oidc.BasicSignatureSigningConfiguration"
+        p:signingCredentials="#{getObject('shibboleth.oidc.SigningCredentialsToPublish') ?: getObject('shibboleth.oidc.SigningCredentialsFactory')}"/>
+    
+    <!--  Encryption keys to publish. -->   
+    <bean id="shibboleth.oidc.PublishKeySetDecryptionConfiguration" 
+        parent="shibboleth.oidc.BasicDecryptionConfiguration">
+        <property name="KEKCredentialResolver">
+            <bean id="defaultOIDCKeyDecryptionCredentialResolver"
+                    class="net.shibboleth.oidc.security.credential.impl.ReturnAllCollectionJOSEObjectCredentialResolver"
+                    c:credentials="#{getObject('shibboleth.oidc.EncryptionCredentialsToPublish') ?: getObject('shibboleth.oidc.EncryptionCredentialsToPublishFactory')}"/>
+        </property>    
+    </bean>
+    
+    <bean id="shibboleth.oidc.EncryptionCredentialsToPublishFactory"
+        class="net.shibboleth.oidc.profile.config.CredentialsListFactory"
+        c:_0="#{getObject('shibboleth.oidc.EncryptionCredentials')}" />
+     
+
+    <bean id="shibboleth.oidc.DefaultSecurityConfiguration"
+        class="net.shibboleth.oidc.profile.config.JSONSecurityConfiguration">
+        <property name="jwtSignatureSigningConfiguration">
+            <ref bean="#{'%{idp.oidc.signing.config:shibboleth.oidc.SigningConfiguration}'.trim()}" />
+        </property>
+        <property name="jwtEncryptionConfiguration">
+            <ref bean="#{'%{idp.oidc.encryption.config:shibboleth.oidc.EncryptionConfiguration}'.trim()}" />
+        </property>
+        <property name="jwtDecryptionConfiguration">
+            <ref bean="#{'%{idp.oidc.decryption.config:shibboleth.oidc.DecryptionConfiguration}'.trim()}" />
+        </property>
+        <property name="jwtSignatureValidationConfiguration">
+            <ref bean="#{'%{idp.oidc.validation.config:shibboleth.oidc.SignatureValidationConfiguration}'.trim()}" />
+        </property>
+    </bean>
+    
+    <bean id="shibboleth.oidc.SigningConfiguration"
+        parent="shibboleth.oidc.BasicSignatureSigningConfiguration"
+        p:signingCredentials-ref="shibboleth.oidc.SigningCredentialsFactory">
+        <property name="signatureAlgorithms">
+            <list>
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_RS_256" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_RS_384" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_RS_512" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_HS_256" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_HS_384" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_HS_512" />
+            </list>
+        </property>
+    </bean>
+
+    <bean id="shibboleth.oidc.EncryptionConfiguration" parent="shibboleth.oidc.BasicEncryptionConfiguration"
+        p:KEKCredentialResolver-ref="defaultOIDCKeyEncryptionCredentialResolver"
+        p:contentEncryptionKeyCredentialResolver-ref="defaultOIDCContentEncryptionKeyCredentialResolver">
+        <property name="keyTransportEncryptionAlgorithms">
+            <list>
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.KeyManagementConstants.ALGO_ID_ALG_RSA_1_5" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.KeyManagementConstants.ALGO_ID_ALG_AES_128_KW" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.KeyManagementConstants.ALGO_ID_ALG_AES_192_KW" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.KeyManagementConstants.ALGO_ID_ALG_AES_256_KW" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.KeyManagementConstants.ALGO_ID_ALG_AES_128_GCM_KW" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.KeyManagementConstants.ALGO_ID_ALG_AES_192_GCM_KW" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.KeyManagementConstants.ALGO_ID_ALG_AES_256_GCM_KW" />
+            </list>
+        </property>
+        <property name="dataEncryptionAlgorithms">
+            <list>
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.EncryptionConstants.ALGO_ID_ENC_ALG_A192CBC_HS384" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.EncryptionConstants.ALGO_ID_ENC_ALG_A128GCM" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.EncryptionConstants.ALGO_ID_ENC_ALG_A192GCM" />
+                <util:constant
+                    static-field="net.shibboleth.oidc.jwa.support.EncryptionConstants.ALGO_ID_ENC_ALG_A256GCM" />
+            </list>
+        </property>
+    </bean>
+
+    <bean id="shibboleth.oidc.SigningCredentialsFactory"
+        class="net.shibboleth.oidc.profile.config.CredentialsListFactory"
+        c:_0="#{getObject('shibboleth.oidc.SigningCredentials')}" />
+        
+    <bean id="defaultOIDCKeyEncryptionCredentialResolver"
+        class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
+        <constructor-arg>
+            <list>
+                <!-- Used by the OP -->
+                <bean id="ClientInformationCredentialResolver"
+                    class="net.shibboleth.oidc.security.credential.impl.ClientInformationCredentialResolver"
+                    p:remoteJwkSetCache-ref="shibboleth.oidc.RemoteJwkSetCache" 
+                    p:keyFetchInterval="%{idp.oidc.provider.keyfetch.interval:PT30M}"/>
+                <!--  Used by the RP -->
+                <bean id="OIDCProviderMetadataCredentialResolver"
+                    class="net.shibboleth.oidc.security.credential.impl.ProviderMetadataCredentialResolver"
+                    p:remoteJwkSetCache-ref="shibboleth.oidc.RemoteJwkSetCache"
+                    p:keyFetchInterval="%{idp.oidc.provider.keyfetch.interval:PT30M}" />
+                <bean id="ClientSecretCriterionCredentialResolver"
+                    class="net.shibboleth.oidc.security.credential.impl.ClientSecretCriterionCredentialResolver"/>
+            </list>
+        </constructor-arg>
+    </bean>
+    
+     <bean id="defaultOIDCContentEncryptionKeyCredentialResolver"
+        class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
+        <constructor-arg>
+            <list>
+                <!-- Used by the OP -->
+                <bean id="ClientInformationCredentialResolver"
+                    class="net.shibboleth.oidc.security.credential.impl.ClientInformationCredentialResolver"
+                    p:remoteJwkSetCache-ref="shibboleth.oidc.RemoteJwkSetCache" 
+                    p:keyFetchInterval="%{idp.oidc.provider.keyfetch.interval:PT30M}"/>
+                <!--  Used by the RP -->
+                <bean id="ClientSecretCriterionCredentialResolver"
+                    class="net.shibboleth.oidc.security.credential.impl.ClientSecretCriterionCredentialResolver"/>
+            </list>
+        </constructor-arg>
+    </bean>   
+    
+    <bean id="shibboleth.oidc.DecryptionConfiguration"
+        parent="shibboleth.oidc.BasicDecryptionConfiguration"
+        p:KEKCredentialResolver-ref="defaultOIDCKeyDecryptionCredentialResolver"
+        p:contentEncryptionKeyCredentialResolver-ref="defaultOIDCContentDecryptionKeyCredentialResolver">
+    </bean>
+    
+    <!-- 
+        A resolver to public/private key encryption keys global to the RP 
+    -->
+    <bean id="defaultOIDCKeyDecryptionCredentialResolver"
+        class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
+        <constructor-arg>
+            <list>
+                <bean class="net.shibboleth.oidc.security.credential.impl.LocalJOSEObjectCredentialResolver">
+                    <constructor-arg name="localCredentialResolver">
+                        <bean
+                            class="net.shibboleth.oidc.security.credential.impl.CollectionJOSEObjectCredentialResolver"
+                            c:credentials-ref="shibboleth.oidc.EncryptionCredentials" />
+                    </constructor-arg>
+                </bean>
+                <bean id="CriterionCredentialResolver"
+                    class="net.shibboleth.oidc.security.credential.impl.ClientSecretCriterionCredentialResolver"/>
+            </list>
+        </constructor-arg>
+    </bean>
+    
+    <bean id="defaultOIDCContentDecryptionKeyCredentialResolver"
+        class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
+        <constructor-arg>
+            <list>
+                <bean id="CriterionCredentialResolver"
+                    class="net.shibboleth.oidc.security.credential.impl.ClientSecretCriterionCredentialResolver"/>
+            </list>
+        </constructor-arg>
+    </bean>    
+    
+    <bean id="shibboleth.oidc.SignatureValidationConfiguration" 
+        parent="shibboleth.oidc.BasicSignatureValidationConfiguration"
+        p:signatureTrustEngine-ref="ExplicitKeySignedJWTTrustEngine"/>
+        
+        
+    <bean id="ExplicitKeySignedJWTTrustEngine"
+        class="net.shibboleth.oidc.security.impl.ExplicitKeySignedJWTTrustEngine"
+        c:resolver-ref="defaultSignedJWTTrustedCredentialResolver"
+        c:JOSEObjectResolver-ref="defaultSignedJWTJOSEHeaderCredentialResolver" />
+        
+    <!-- A resolver for resolving 'untrusted' credentials out of the JOSE header of JWTs -->
+    <bean id="defaultSignedJWTJOSEHeaderCredentialResolver"
+        class="net.shibboleth.oidc.security.credential.impl.BasicJOSEObjectCredentialResolver" />
+    
+     <!-- A resolver for resolving trusted credentials to match against those resolved from the JWT -->
+    <bean id="defaultSignedJWTTrustedCredentialResolver"
+        class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
+        <constructor-arg>
+            <list>
+                <!-- Used by the OP -->
+                <bean id="ClientInformationCredentialResolver"
+                    class="net.shibboleth.oidc.security.credential.impl.ClientInformationCredentialResolver"
+                    p:remoteJwkSetCache-ref="shibboleth.oidc.RemoteJwkSetCache" 
+                    p:keyFetchInterval="%{idp.oidc.provider.keyfetch.interval:PT30M}"/>
+                <!--  Used by the RP -->
+                <bean id="OIDCProviderMetadataCredentialResolver"
+                    class="net.shibboleth.oidc.security.credential.impl.ProviderMetadataCredentialResolver"
+                    p:remoteJwkSetCache-ref="shibboleth.oidc.RemoteJwkSetCache" 
+                    p:keyFetchInterval="%{idp.oidc.provider.keyfetch.interval:PT30M}"/>
+                <bean id="CriterionCredentialResolver"
+                    class="net.shibboleth.oidc.security.credential.impl.ClientSecretCriterionCredentialResolver"/>
+            </list>
+        </constructor-arg>
+    </bean>
+
+
+</beans>
diff --git a/oidc-common-profile-api/src/test/java/net/shibboleth/oidc/profile/config/CredentialsListFactoryTest.java b/oidc-common-profile-api/src/test/java/net/shibboleth/oidc/profile/config/CredentialsListFactoryTest.java
new file mode 100644
index 0000000..3fe818d
--- /dev/null
+++ b/oidc-common-profile-api/src/test/java/net/shibboleth/oidc/profile/config/CredentialsListFactoryTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.oidc.profile.config;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.opensaml.security.credential.Credential;
+import org.testng.annotations.Test;
+
+import net.shibboleth.oidc.security.credential.BasicJWKCredential;
+
+/** Tests for the {@link CredentialsListFactory}.*/
+public class CredentialsListFactoryTest {
+    
+    /** 
+     * Test with a null input list.
+     * 
+     * @throws Exception on error
+     */
+    @Test
+    public void testNullInputList() throws Exception {
+        final CredentialsListFactory factory = new CredentialsListFactory(null);
+        factory.afterPropertiesSet();
+        final List<Credential> creds = factory.createInstance();
+        assertNotNull(creds);
+        assertTrue(creds.isEmpty());
+    }
+    
+    
+    /** 
+     * Test with an input list with a null element.
+     * 
+     * @throws Exception on error
+     */
+    @Test
+    public void testInputListWithNullElement() throws Exception {
+        final var credsIn = new ArrayList<Credential>();
+        credsIn.add(null);
+        final CredentialsListFactory factory = new CredentialsListFactory(credsIn);
+        factory.afterPropertiesSet();
+        final List<Credential> creds = factory.createInstance();
+        assertNotNull(creds);
+        assertTrue(creds.isEmpty());
+    }
+    
+    /** 
+     * Test with an input list with one null element.
+     * 
+     * @throws Exception on error
+     */
+    @Test
+    public void testInputListWithOneNullElement() throws Exception {
+        final var credsIn = new ArrayList<Credential>();
+        credsIn.add(null);
+        credsIn.add(new BasicJWKCredential());
+        final CredentialsListFactory factory = new CredentialsListFactory(credsIn);
+        factory.afterPropertiesSet();
+        final List<Credential> creds = factory.createInstance();
+        assertNotNull(creds);
+        assertTrue(creds.size()==1);
+    }
+
+}
diff --git a/oidc-common-profile-api/src/test/java/net/shibboleth/oidc/profile/config/navigate/ProxyAwareDefaultOIDCAuthenticationContextClassLookupFunctionTest.java b/oidc-common-profile-api/src/test/java/net/shibboleth/oidc/profile/config/navigate/ProxyAwareDefaultOIDCAuthenticationContextClassLookupFunctionTest.java
new file mode 100644
index 0000000..244abf8
--- /dev/null
+++ b/oidc-common-profile-api/src/test/java/net/shibboleth/oidc/profile/config/navigate/ProxyAwareDefaultOIDCAuthenticationContextClassLookupFunctionTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.oidc.profile.config.navigate;
+
+import static org.testng.Assert.assertTrue;
+
+import java.security.Principal;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.RequestedPrincipalContext;
+import net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal;
+import net.shibboleth.oidc.authn.principal.AuthenticationContextClassReferencePrincipal;
+
+/** Tests for ProxyAwareDefaultOIDCAuthenticationContextClassLookupFunction.*/
+public class ProxyAwareDefaultOIDCAuthenticationContextClassLookupFunctionTest {
+    
+    private ProxyAwareDefaultOIDCAuthenticationContextClassRequestLookupFunction function;
+    
+    private ProfileRequestContext prc;
+    
+    private ProfileRequestContext nestedPrc;
+    
+    private AuthenticationContext ac;
+    
+    private RequestedPrincipalContext rpc;
+    
+    @BeforeMethod
+    public void setup() {
+        function = new ProxyAwareDefaultOIDCAuthenticationContextClassRequestLookupFunction();
+        prc = new ProfileRequestContext();
+        ac = prc.getSubcontext(AuthenticationContext.class, true);
+        nestedPrc = ac.getSubcontext(ProfileRequestContext.class, true);
+        rpc = ac.getSubcontext(RequestedPrincipalContext.class, true);
+    }
+    
+    @Test
+    public void testSingleMappingSuccess() {
+        
+        
+        final List<Principal> requestedPrincipals = 
+                List.of(new AuthnContextClassRefPrincipal("http://example.org/ac/classes/mfa"));
+        rpc.setRequestedPrincipals(requestedPrincipals);
+        rpc.setOperator("exact");
+        
+        final Map<Principal,Collection<Principal>> mappings = new HashMap<>();
+        mappings.put(new AuthnContextClassRefPrincipal("http://example.org/ac/classes/mfa"), 
+                List.of(new AuthenticationContextClassReferencePrincipal("http://proxy.example.org/ac/classes/mfa")));
+        function.setMappings(mappings);
+        
+        final Collection<AuthenticationContextClassReferencePrincipal> mapped = function.apply(nestedPrc);
+        System.out.println(mapped);
+        assertTrue(mapped.contains(
+                        new AuthenticationContextClassReferencePrincipal("http://proxy.example.org/ac/classes/mfa")));
+        
+    }
+    
+    @Test
+    public void testMappedAndPassedThroughSuccess() {
+        
+        
+        // The second principal is passed-through as no mapping exists
+        final List<Principal> requestedPrincipals = 
+                List.of(new AuthnContextClassRefPrincipal("http://example.org/ac/classes/mfa"),
+                        new AuthnContextClassRefPrincipal("http://example.org/ac/classes/pass-through"));
+        rpc.setRequestedPrincipals(requestedPrincipals);
+        rpc.setOperator("exact");
+        
+        final Map<Principal,Collection<Principal>> mappings = new HashMap<>();
+        mappings.put(new AuthnContextClassRefPrincipal("http://example.org/ac/classes/mfa"), 
+                List.of(new AuthenticationContextClassReferencePrincipal("http://proxy.example.org/ac/classes/mfa")));
+        function.setMappings(mappings);
+        
+        final Collection<AuthenticationContextClassReferencePrincipal> mapped = function.apply(nestedPrc);
+        System.out.println(mapped);
+        assertTrue(mapped.contains(
+                        new AuthenticationContextClassReferencePrincipal("http://proxy.example.org/ac/classes/mfa")));
+        assertTrue(mapped.contains(
+                new AuthenticationContextClassReferencePrincipal("http://example.org/ac/classes/pass-through")));
+        
+    }
+    
+    @Test
+    public void testMultipleMappedAndPassedThroughSuccess() {
+        
+        
+        // The second principal is passed-through as no mapping exists
+        final List<Principal> requestedPrincipals = 
+                List.of(new AuthnContextClassRefPrincipal("http://example.org/ac/classes/mfa"),
+                        new AuthnContextClassRefPrincipal("http://example.org/ac/classes/pass-through"));
+        rpc.setRequestedPrincipals(requestedPrincipals);
+        rpc.setOperator("exact");
+        
+        final Map<Principal,Collection<Principal>> mappings = new HashMap<>();
+        mappings.put(new AuthnContextClassRefPrincipal("http://example.org/ac/classes/mfa"), 
+                List.of(new AuthenticationContextClassReferencePrincipal("http://proxy.example.org/ac/classes/mfa"),
+                        new AuthenticationContextClassReferencePrincipal(
+                                "http://proxy.two.example.org/ac/classes/mfa")));
+        function.setMappings(mappings);
+        
+        final Collection<AuthenticationContextClassReferencePrincipal> mapped = function.apply(nestedPrc);
+        System.out.println(mapped);
+        assertTrue(mapped.contains(
+                        new AuthenticationContextClassReferencePrincipal("http://proxy.example.org/ac/classes/mfa")));
+        assertTrue(mapped.contains(
+                new AuthenticationContextClassReferencePrincipal("http://proxy.two.example.org/ac/classes/mfa")));
+        assertTrue(mapped.contains(
+                new AuthenticationContextClassReferencePrincipal("http://example.org/ac/classes/pass-through")));
+        
+    }
+
+}
diff --git a/oidc-common-profile-api/src/test/java/net/shibboleth/oidc/profile/config/navigate/ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunctionTest.java b/oidc-common-profile-api/src/test/java/net/shibboleth/oidc/profile/config/navigate/ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunctionTest.java
new file mode 100644
index 0000000..401d057
--- /dev/null
+++ b/oidc-common-profile-api/src/test/java/net/shibboleth/oidc/profile/config/navigate/ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunctionTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.oidc.profile.config.navigate;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.RequestedPrincipalContext;
+import net.shibboleth.idp.saml.authn.principal.AuthenticationMethodPrincipal;
+
+/** Tests for ProxyAwareDefaultOIDCAuthenticationContextClassLookupFunction.*/
+public class ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunctionTest {
+    
+    private ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunction function;
+    
+    private ProfileRequestContext prc;
+    
+    private ProfileRequestContext nestedPrc;
+    
+    private AuthenticationContext ac;
+    
+    private RequestedPrincipalContext rpc;
+    
+    @BeforeMethod
+    public void setup() {        
+        prc = new ProfileRequestContext();
+        ac = prc.getSubcontext(AuthenticationContext.class, true);
+        nestedPrc = ac.getSubcontext(ProfileRequestContext.class, true);
+        rpc = ac.getSubcontext(RequestedPrincipalContext.class, true);
+    }
+    
+    @Test
+    public void testSingleMappingSuccess() {
+        
+        final Map<String,Collection<Principal>> mappings = new HashMap<>();
+        mappings.put(
+                "pwd", 
+                List.of(new AuthenticationMethodPrincipal
+                        ("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport")));
+
+        function = new ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunction(mappings);
+        
+        final Collection<Principal> mapped = function.apply(List.of("pwd"));
+        assertTrue(mapped.contains(
+                        new AuthenticationMethodPrincipal
+                        ("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport")));
+        
+    }
+    
+    @Test
+    public void testSingleMappingSuccess_NotMappedIgnored() {
+        
+        
+        final Map<String,Collection<Principal>> mappings = new HashMap<>();
+        mappings.put(
+                "pwd", 
+                List.of(new AuthenticationMethodPrincipal
+                        ("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport")));
+        function = new ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunction(mappings);;
+        
+        // OTP should not be in the result
+        final Collection<Principal> mapped = function.apply(List.of("pwd","otp"));
+        assertEquals(mapped.size(), 1);
+        assertTrue(mapped.contains(new AuthenticationMethodPrincipal
+                        ("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport")));
+       
+    }
+    
+    @Test
+    public void testSingleMappingSuccess_EmptyInput() {
+        
+        // OTP should not be in the result
+        final Collection<Principal> mapped = function.apply(Collections.emptyList());
+        assertEquals(mapped.size(), 0);
+    }
+    
+    @Test
+    public void testSingleMappingSuccess_NullMappings() {
+        
+        function = new ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunction(null);
+        
+        // OTP should not be in the result
+        final Collection<Principal> mapped = function.apply(Collections.emptyList());
+        assertEquals(mapped.size(), 0);
+    }
+    
+    @Test
+    public void testSingleMappingSuccess_EmptyMapping() {
+        
+        final Map<String,Collection<Principal>> mappings = new HashMap<>();
+        mappings.put("pwd", Collections.emptyList());
+        function = new ProxyAwareDefaultOIDCAuthenticationContextClassResponseLookupFunction(mappings);
+        
+        // OTP should not be in the result
+        final Collection<Principal> mapped = function.apply(Collections.emptyList());
+        assertEquals(mapped.size(), 0);
+    }
+
+
+}

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


More information about the commits mailing list