[java-plugin-shibd-oidc] 14/14: WIP: JSHIBDOIDC-35 - Add Storage Service ClientSecret Credential Resolver

Codeberg noreply at shibboleth.net
Tue Nov 3 15:34:28 UTC 2026


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

codeberg pushed a commit to branch dev/JSHIBDOIDC-31-cred-resolver
in repository java-plugin-shibd-oidc.

View the commit online:
https://codeberg.org/Shibboleth/java-plugin-shibd-oidc/commit/eebe1022d39343eccc5d88060b6a4d4c05e22545

commit eebe1022d39343eccc5d88060b6a4d4c05e22545
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Oct 23 16:49:56 2026 +0100

    WIP:  JSHIBDOIDC-35 - Add Storage Service ClientSecret Credential
    Resolver
    
     - Refactor to use new credential resolver storage service
     - Add better defaults for client_secret resolution
     - Remove asymmetric key defaults.
     - Add flows tests for different ID Token and Request Object
    cryprographic algorithms
    
    https://shibboleth.atlassian.net/browse/JSHIBDOIDC-35
---
 .../shibboleth/sp/oidc/testing/TestConstants.java  |   6 +
 .../META-INF/net.shibboleth.idp/postconfig.xml     |   6 -
 .../idp/flows/sp/consumer/oidc/oidc-beans.xml      |  11 +-
 .../net/shibboleth/sp/service/agent/postconfig.xml | 105 ++++++++++--
 .../sp/service/credentials/postconfig.xml          |  85 ++++------
 .../shibboleth/idp/module/conf/sp/oidc.properties  |   5 -
 .../idp/module/credentials/sp/client-secrets.txt   |   2 +
 .../net/shibboleth/sp/oidc/conf/module.properties  |   4 +
 .../net/shibboleth/sp/oidc/conf/plugin.properties  |   2 +-
 .../oidc/flows/OIDCSessionInitiatorFlowTest.java   |  71 ++++++++
 .../sp/oidc/flows/OIDCTokenConsumerFlowTest.java   |  92 ++++++++++-
 ...DCEnvironmentApplicationContextInitializer.java |   2 -
 ...openid-configuration-a128kw-request-object.json |  62 +++++++
 .../openid-configuration-hs256-request-object.json |  62 +++++++
 .../net/shibboleth/idp/module/conf/sp/agents.xml   |   8 +-
 .../shibboleth/idp/module/conf/sp/credentials.xml  |  51 ++++++
 .../idp/module/conf/sp/oidc-test-agents.xml        | 179 ++++++++++++++-------
 .../idp/module/conf/sp/oidc-test.properties        |  12 --
 ...{sp-encryption-rsa.jwk => sp-encryption-rs.jwk} |   0
 .../idp/module/credentials/sp/sp-encryption.crt    |  19 ---
 .../idp/module/credentials/sp/sp-encryption.key    |  27 ----
 .../net/shibboleth/sp/oidc-test-beans.xml          |  11 +-
 .../impl/ClientSecretCredentialResolver.java       |  14 +-
 ...StaticFilteringCriterionCredentialResolver.java |  12 +-
 .../impl/ClientSecretStorageServiceResolver.java   |  34 ++--
 25 files changed, 648 insertions(+), 234 deletions(-)

diff --git a/sp-oidc-api/src/test/java/net/shibboleth/sp/oidc/testing/TestConstants.java b/sp-oidc-api/src/test/java/net/shibboleth/sp/oidc/testing/TestConstants.java
index 7d6aeb9..ea801ec 100644
--- a/sp-oidc-api/src/test/java/net/shibboleth/sp/oidc/testing/TestConstants.java
+++ b/sp-oidc-api/src/test/java/net/shibboleth/sp/oidc/testing/TestConstants.java
@@ -49,6 +49,12 @@ public final class TestConstants {
     /** Application ID for an application that uses the request object in the request. */
     public static final String APPLICATION_ID_REQUEST_OBJECT = "test-oidc-application-with-ro";    
     
+    /** Application ID for an application that uses an encrypted request object in the request. */
+    public static final String APPLICATION_ID_REQUEST_OBJECT_ENCRYPTED = "test-oidc-application-with-encrypted-ro";   
+    
+    /** Application ID for an application that uses a key wrapped encrypted request object in the request. */
+    public static final String APPLICATION_ID_REQUEST_OBJECT_KWENCRYPTED = "test-oidc-application-with-kwencrypted-ro";
+    
     /** Default Application ID that supports all default profiles and no special configuration. */
     public static final String APPLICATION_ID = "test-oidc-application-with-default-profile";
     
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 1347b0c..85f8262 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -166,11 +166,5 @@
         </constructor-arg>
     </bean>   
     
-    <!-- Global abstract bean for client secret resolvers which are specified in a number of different bean files -->
-    <!-- TODO this is not just a client secret resolver, it resolves everything from the credential service! CHANGE THE NAME AND THING WHERE USED -->
-    <bean id="shibboleth.sp.oidc.ClientSecretCredentialResolver" class="net.shibboleth.sp.jose.config.impl.ClientSecretCredentialResolver"
-       c:resolver-ref="shibboleth.sp.CredentialResolverService" scope="prototype" abstract="true"
-       c:requestSupplier-ref="shibboleth.HttpServletRequestSupplier"/>
-    
 
 </beans>
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-beans.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-beans.xml
index 8e3258f..45d2205 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-beans.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-beans.xml
@@ -91,8 +91,11 @@
                             p:securityParametersContextLookupStrategy-ref="shibboleth.ChildLookupOrCreate.SecurityParametersFromOAuth2ClientAuthenticationContext"
                             p:jwtBearerExpiryOffset="%{sp.oidc.authenticationMethod.jwt.expiryOffset:PT30S}"
                             p:issuerLookupStrategy-ref="shibboleth.ClientIdLookup.Simple">
-                            <property name="clientSecretResolver">
-                                <bean id="ClientSecretCredentialResolver" parent="shibboleth.sp.oidc.ClientSecretCredentialResolver"/>
+                            <property name="clientSecretResolver"> <!-- TODO remove this -->
+									    <bean class="net.shibboleth.sp.jose.config.impl.ClientSecretCredentialResolver"
+									       c:resolver-ref="shibboleth.sp.CredentialResolverService" 
+									       scope="prototype" abstract="true"
+									       c:requestSupplier-ref="shibboleth.HttpServletRequestSupplier"/>                     
                             </property>
                           </bean> 
                     </list>
@@ -178,6 +181,10 @@
                             
                         <bean class="net.shibboleth.oidc.security.impl.JWTMessageSignatureSecurityHandler"
                             scope="prototype">
+                            <property name="additionalCriteriaLookupStrategy">
+                                <!-- Turn off additional criteria. Specifically the PRC criterion is added by the resolver service -->
+                                <bean parent="shibboleth.Functions.Constant" c:_0="#{null}"/>
+                            </property>
                             <property name="jwtTokenLookupStrategy">
                                 <bean
                                     class="net.shibboleth.profile.context.navigate.SpringExpressionContextLookupFunction"
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml
index c21d8c6..74e0dae 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml
@@ -37,13 +37,6 @@
             </list>
         </property>        
     </bean>
-
-    <!-- Credential bean factories -->
-    <bean id="shibboleth.oidc.ClientSecretCredential" abstract="true"
-        class="net.shibboleth.oidc.security.credential.BasicClientSecretCredentialFactoryBean" />    
-        
-    <bean id="shibboleth.oidc.JWKCredential" abstract="true"
-        class="net.shibboleth.oidc.security.credential.BasicJWKCredentialFactoryBean" />
     
     <!-- Singleton JWK cache -->
     <bean id="shibboleth.oidc.RemoteJwkSetCache"
@@ -139,12 +132,19 @@
         class="net.shibboleth.sp.jose.config.impl.BasicSignatureSigningConfiguration"
         c:resolver-ref="shibboleth.sp.CredentialResolverBridge" />
         
-
+    <!-- 
+        In addition to the signature signing configuration bridge bean above, a new parent for
+    	direct resolution of local credentials via the credential resolver service.
+    -->
+    <bean id="shibboleth.sp.oidc.LocalCredentialResolver" abstract="true"
+        class="net.shibboleth.sp.jose.config.impl.StaticFilteringCriterionCredentialResolver"
+        c:resolver-ref="shibboleth.sp.CredentialResolverBridge"/>
         
    <!-- 
         Security configuration beans.
     -->
     
+    <!--TODO namespace with sp. in them? -->
     <bean id="shibboleth.oidc.DefaultSecurityConfiguration"
         class="net.shibboleth.oidc.profile.config.JSONSecurityConfiguration" c:clockSkew="%{sp.policy.clockSkew:PT1M}">
         <constructor-arg name="idGenerator">
@@ -261,11 +261,31 @@
         class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
         <constructor-arg>
             <list>
+            
                 <bean id="OIDCProviderMetadataCredentialResolver"
                     class="net.shibboleth.oidc.security.credential.impl.ProviderMetadataCredentialResolver"
                     c:remoteJwkSetCache-ref="shibboleth.oidc.RemoteJwkSetCache"
                     c:keyFetchInterval="%{sp.oidc.keyfetch.interval:PT30M}" />
-                <bean id="ClientSecretCredentialResolver" parent="shibboleth.sp.oidc.ClientSecretCredentialResolver"/>
+                    
+                <!-- A local encryption resolver for shared secret key encryption e.g. using A128KW and a client_secret -->
+                <bean id="localKeyEncryptionResolver" parent="shibboleth.sp.oidc.LocalCredentialResolver"
+                    c:criteriaPassthrough="true">
+                    <constructor-arg name="criteria">
+                        <set>
+                            <bean class="org.opensaml.security.criteria.UsageCriterion">
+                                <constructor-arg>
+                                    <util:constant static-field="org.opensaml.security.credential.UsageType.ENCRYPTION" />
+                                </constructor-arg>
+                            </bean>
+                            <bean class="org.opensaml.saml.criterion.ProtocolCriterion">
+                                <constructor-arg>
+                                    <util:constant static-field="net.shibboleth.oidc.saml.xmlobject.Constants.OIDC_PROTOCOL_URI" />
+                                </constructor-arg>
+                            </bean>
+                        </set>
+                    </constructor-arg>
+                </bean>
+                
             </list>
         </constructor-arg>
     </bean>
@@ -274,7 +294,24 @@
         class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
         <constructor-arg>
             <list>
-                <bean id="ClientSecretCredentialResolver" parent="shibboleth.sp.oidc.ClientSecretCredentialResolver"/>
+                 <!-- A local encryption resolver for shared secret Direct encryption -->
+                <bean id="localContentEncryptionResolver" parent="shibboleth.sp.oidc.LocalCredentialResolver"
+                    c:criteriaPassthrough="true">
+                    <constructor-arg name="criteria">
+                        <set>
+                            <bean class="org.opensaml.security.criteria.UsageCriterion">
+                                <constructor-arg>
+                                    <util:constant static-field="org.opensaml.security.credential.UsageType.ENCRYPTION" />
+                                </constructor-arg>
+                            </bean>
+                            <bean class="org.opensaml.saml.criterion.ProtocolCriterion">
+                                <constructor-arg>
+                                    <util:constant static-field="net.shibboleth.oidc.saml.xmlobject.Constants.OIDC_PROTOCOL_URI" />
+                                </constructor-arg>
+                            </bean>
+                        </set>
+                    </constructor-arg>
+                </bean>
             </list>
         </constructor-arg>
     </bean>   
@@ -299,11 +336,12 @@
                 credential resolvers to use in matching local keys.
              -->
                 <bean id="localDecryptionCredentialResolver" 
-                    class="net.shibboleth.oidc.security.credential.impl.LocalJOSEObjectCredentialResolver">
+                    class="net.shibboleth.oidc.security.credential.impl.LocalJOSEObjectCredentialResolver"
+                    c:criteriaPassthrough="true">
                     <constructor-arg name="localCredentialResolver">
                             <!-- This bridge resolves decryption keys using the CredentialResolver service while adding needed criteria. -->
-						    <bean id="localDecryptionKEKResolver" class="net.shibboleth.sp.jose.config.impl.StaticFilteringCriterionCredentialResolver"
-						            c:resolver-ref="shibboleth.sp.CredentialResolverBridge">
+						    <bean id="localDecryptionKEKResolver" parent="shibboleth.sp.oidc.LocalCredentialResolver"
+						          c:criteriaPassthrough="true">
 						        <constructor-arg name="criteria">
 						            <set>
 						                <bean class="org.opensaml.security.criteria.UsageCriterion">
@@ -321,8 +359,6 @@
 						    </bean>
                     </constructor-arg>
                 </bean>
-                <!-- TODO why do we need this seperate one if we use the credential resolver service above? -->
-               <!--  <bean id="ClientSecretCredentialResolver" parent="shibboleth.sp.oidc.ClientSecretCredentialResolver"/> -->
             </list>
         </constructor-arg>
     </bean>
@@ -331,7 +367,24 @@
         class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
         <constructor-arg>
             <list>
-               <bean id="ClientSecretCredentialResolver" parent="shibboleth.sp.oidc.ClientSecretCredentialResolver"/>
+               <!-- A local decryption resolver for shared secret Direct encryption ('dir') -->
+                <bean id="localContentDecryptionResolver" parent="shibboleth.sp.oidc.LocalCredentialResolver"
+                    c:criteriaPassthrough="true">
+                    <constructor-arg name="criteria">
+                        <set>
+                            <bean class="org.opensaml.security.criteria.UsageCriterion">
+                                <constructor-arg>
+                                    <util:constant static-field="org.opensaml.security.credential.UsageType.ENCRYPTION" />
+                                </constructor-arg>
+                            </bean>
+                            <bean class="org.opensaml.saml.criterion.ProtocolCriterion">
+                                <constructor-arg>
+                                    <util:constant static-field="net.shibboleth.oidc.saml.xmlobject.Constants.OIDC_PROTOCOL_URI" />
+                                </constructor-arg>
+                            </bean>
+                        </set>
+                    </constructor-arg>
+                </bean>
             </list>
         </constructor-arg>
     </bean>    
@@ -359,7 +412,25 @@
                     class="net.shibboleth.oidc.security.credential.impl.ProviderMetadataCredentialResolver"
                     c:remoteJwkSetCache-ref="shibboleth.oidc.RemoteJwkSetCache" 
                     c:keyFetchInterval="%{sp.oidc.provider.keyfetch.interval:PT30M}"/>
-                <bean id="ClientSecretCredentialResolver" parent="shibboleth.sp.oidc.ClientSecretCredentialResolver"/>
+                    
+                 <!-- A local signing key resolver for symmetric signing keys -->
+                <bean id="localSigningCredentialResolver" parent="shibboleth.sp.oidc.LocalCredentialResolver"
+                    c:criteriaPassthrough="true">
+                    <constructor-arg name="criteria">
+                        <set>
+                            <bean class="org.opensaml.security.criteria.UsageCriterion">
+                                <constructor-arg>
+                                    <util:constant static-field="org.opensaml.security.credential.UsageType.SIGNING" />
+                                </constructor-arg>
+                            </bean>
+                            <bean class="org.opensaml.saml.criterion.ProtocolCriterion">
+                                <constructor-arg>
+                                    <util:constant static-field="net.shibboleth.oidc.saml.xmlobject.Constants.OIDC_PROTOCOL_URI" />
+                                </constructor-arg>
+                            </bean>
+                        </set>
+                    </constructor-arg>
+                </bean>
             </list>
         </constructor-arg>
     </bean>
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/credentials/postconfig.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/credentials/postconfig.xml
index 410cdb3..f4aa1f4 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/credentials/postconfig.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/credentials/postconfig.xml
@@ -12,13 +12,18 @@
        default-init-method="initialize"
        default-destroy-method="destroy">
        
+    <!-- Credential parent beans -->
+        
+    <bean id="shibboleth.oidc.JWKCredential" abstract="true"
+        class="net.shibboleth.oidc.security.credential.BasicJWKCredentialFactoryBean" />
+       
     <!--  
         Credential Resolvers that are auto-wired into the credential resolver service 
         (shibboleth.sp.CredentialResolverService). 
     -->
 
-    <!-- Resolver parent beans. -->
-
+    <!-- OIDC specific resolver parent beans. -->    
+    
     <bean id="shibboleth.sp.oidc.CredentialResolver.MapBased"
             parent="shibboleth.sp.CredentialResolver.MapBased" abstract="true">
         <property name="protocols">
@@ -26,23 +31,11 @@
         </property>
     </bean>
     
-    <bean id="shibboleth.sp.CredentialResolver.StorageBased.JWK"
-        class="net.shibboleth.sp.jose.credential.impl.JWKCredentialStorageService" abstract="true"
-        p:storageService-ref="shibboleth.sp.CredentialStorage.ReadOnly"
-        p:velocityEngine-ref="shibboleth.VelocityEngine"
-        p:contextTemplate="${protocol}" />
-        
-    <bean id="shibboleth.sp.CredentialResolver.StorageBased.JWKS"
-        class="net.shibboleth.sp.jose.credential.impl.JWKCredentialStorageService" abstract="true"
+    <bean id="shibboleth.sp.oidc.CredentialResolver.StorageBased.JWK"
+        class="net.shibboleth.sp.jose.credential.impl.JWKCredentialStorageServiceResolver" abstract="true"
         p:storageService-ref="shibboleth.sp.CredentialStorage.ReadOnly"
         p:velocityEngine-ref="shibboleth.VelocityEngine"
-        p:contextTemplate="${protocol}" />        
-
-    
-    <!-- OIDC specific resolver parent beans. -->
-    
-    <bean id="shibboleth.sp.oidc.CredentialResolver.StorageBased.JWK"
-                    parent="shibboleth.sp.CredentialResolver.StorageBased.JWK" abstract="true">
+        p:contextTemplate="${protocol}">
         <property name="protocols">
             <util:constant static-field="net.shibboleth.oidc.saml.xmlobject.Constants.OIDC_PROTOCOL_URI" />
         </property>
@@ -59,7 +52,10 @@
     </bean>
     
     <bean id="shibboleth.sp.oidc.CredentialResolver.StorageBased.JWKS"
-                    parent="shibboleth.sp.CredentialResolver.StorageBased.JWKS" abstract="true">
+        class="net.shibboleth.sp.jose.credential.impl.JWKCredentialStorageServiceResolver" abstract="true"
+        p:storageService-ref="shibboleth.sp.CredentialStorage.ReadOnly"
+        p:velocityEngine-ref="shibboleth.VelocityEngine"
+        p:contextTemplate="${protocol}">
         <property name="protocols">
             <util:constant static-field="net.shibboleth.oidc.saml.xmlobject.Constants.OIDC_PROTOCOL_URI" />
         </property>
@@ -85,12 +81,28 @@
         <property name="protocols">
             <util:constant static-field="net.shibboleth.oidc.saml.xmlobject.Constants.OIDC_PROTOCOL_URI" />
         </property>
+        <property name="protocolMap">
+            <map>
+                <entry>
+                    <key>
+                        <util:constant static-field="net.shibboleth.oidc.saml.xmlobject.Constants.OIDC_PROTOCOL_URI" />
+                    </key>
+                    <value>oidc</value>
+                </entry>
+            </map>
+        </property>
+    </bean>    
+        
+    <bean id="shibboleth.sp.oidc.CredentialResolver.Static" class="net.shibboleth.sp.credential.impl.StaticCredentialResolver"
+        abstract="true">
+        <property name="protocols">
+            <util:constant static-field="net.shibboleth.oidc.saml.xmlobject.Constants.OIDC_PROTOCOL_URI" />
+        </property>
     </bean>
 
-
-    <!-- Installs a "default" set of OIDC credentials into the resolver service. -->
+    <!-- Installs a "default" Client Secret resolver into the resolver service. -->
    
-    <bean id="shibboleth.sp.oidc.DefaultStorageServiceClientSecretResolver" 
+    <bean id="shibboleth.sp.oidc.DefaultClientSecretResolver" 
         parent="shibboleth.sp.oidc.CredentialResolver.StorageBased.ClientSecret"
         p:clientSecretTemplateString="${transformedRelyingPartyID}">
         <property name="storageService">
@@ -99,37 +111,6 @@
                 p:delimiter="%{sp.oidc.service.clientSecret.delimiter::}"/>
         </property>
     </bean>
-    
-    <bean id="shibboleth.sp.oidc.DefaultCredentialResolver" class="net.shibboleth.sp.credential.impl.StaticCredentialResolver"
-            c:_0="#{getObject('shibboleth.sp.oidc.DefaultCredentials') ?: getObject('DefaultCredentials')}">
-        <property name="order">
-            <util:constant static-field="org.springframework.core.Ordered.LOWEST_PRECEDENCE" />
-        </property>
-        <property name="protocols">
-            <util:constant static-field="net.shibboleth.oidc.saml.xmlobject.Constants.OIDC_PROTOCOL_URI" />
-        </property>
-    </bean>  
-    
-    <bean id="DefaultCredentials" class="org.springframework.beans.factory.config.ListFactoryBean" lazy-init="true">
-        <property name="sourceList">
-            <list>
-                    
-                 <!-- TODO p:usageType comes from the JWK use, not that specified, maybe this should be overriden if specified -->
-                <bean id="shibboleth.oidc.DefaultRSSigningCredential" 
-					parent="shibboleth.oidc.JWKCredential"
-					p:resource="%{sp.oidc.signing.rs.key:#{null}}" 
-					p:throwIfNull="false" />
-
-			    <bean id="shibboleth.oidc.DefaultESSigningCredential" 
-					parent="shibboleth.oidc.JWKCredential"
-					p:resource="%{sp.oidc.signing.es.key:#{null}}" 
-					p:throwIfNull="false" />
 
-			    <bean id="shibboleth.oidc.DefaultRSAEncryptionCredential" 
-					parent="shibboleth.oidc.JWKCredential"
-					p:resource="%{sp.oidc.encryption.rsa.key:#{null}}" p:throwIfNull="false" />
-            </list>
-        </property>
-    </bean>
 
 </beans>
\ No newline at end of file
diff --git a/sp-oidc-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/oidc.properties b/sp-oidc-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/oidc.properties
index bc934a5..62cbae2 100644
--- a/sp-oidc-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/oidc.properties
+++ b/sp-oidc-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/oidc.properties
@@ -1,10 +1,5 @@
 # OIDC-specific RP settings
 
-## Signing and encryption/decryption keys for request objects, id tokens, client authentication, and userinfo response
-#sp.oidc.signing.rs.key = %{idp.home}/credentials/sp/sp-signing-rs.jwk
-#sp.oidc.signing.es.key = %{idp.home}/credentials/sp/sp-signing-es.jwk
-#sp.oidc.encryption.rsa.key = %{idp.home}/credentials/sp/sp-encryption-rsa.jwk
-
 # The OP's keyset document fetch interval i.e. how long should keys from the OP be cached.
 #sp.oidc.keyfetch.interval = PT30M
 
diff --git a/sp-oidc-conf-impl/src/main/resources/net/shibboleth/idp/module/credentials/sp/client-secrets.txt b/sp-oidc-conf-impl/src/main/resources/net/shibboleth/idp/module/credentials/sp/client-secrets.txt
new file mode 100644
index 0000000..a76d62a
--- /dev/null
+++ b/sp-oidc-conf-impl/src/main/resources/net/shibboleth/idp/module/credentials/sp/client-secrets.txt
@@ -0,0 +1,2 @@
+# File used to store client secrets, one entry per line, in the format:
+# {agentID}:{transformedRelyingPartyID}:{secret}
\ No newline at end of file
diff --git a/sp-oidc-conf-impl/src/main/resources/net/shibboleth/sp/oidc/conf/module.properties b/sp-oidc-conf-impl/src/main/resources/net/shibboleth/sp/oidc/conf/module.properties
index b5b1035..555efec 100644
--- a/sp-oidc-conf-impl/src/main/resources/net/shibboleth/sp/oidc/conf/module.properties
+++ b/sp-oidc-conf-impl/src/main/resources/net/shibboleth/sp/oidc/conf/module.properties
@@ -34,3 +34,7 @@ sp.OIDC.5.nonwindows = false
 
 sp.OIDC.6.src = /net/shibboleth/idp/module/bin/lib/json-web-key-generator-0.8.2-jar-with-dependencies.jar
 sp.OIDC.6.dest = bin/net.shibboleth.sp.oidc/lib/json-web-key-generator-0.8.2-jar-with-dependencies.jar
+
+sp.OIDC.7.src = /net/shibboleth/idp/module/credentials/sp/client-secrets.txt
+sp.OIDC.7.dest = credentials/sp/client-secrets.txt
+sp.OIDC.7.replace false
\ No newline at end of file
diff --git a/sp-oidc-conf-impl/src/main/resources/net/shibboleth/sp/oidc/conf/plugin.properties b/sp-oidc-conf-impl/src/main/resources/net/shibboleth/sp/oidc/conf/plugin.properties
index 0ba68a8..55b9d0f 100644
--- a/sp-oidc-conf-impl/src/main/resources/net/shibboleth/sp/oidc/conf/plugin.properties
+++ b/sp-oidc-conf-impl/src/main/resources/net/shibboleth/sp/oidc/conf/plugin.properties
@@ -3,7 +3,7 @@
 plugin.id = net.shibboleth.plugin.shibd.oidc
 
 # Only used when package manifest is not available
-plugin.version = 0.0.2
+plugin.version = 0.0.3
 
 # No prereqs
 #plugin.modules.required =
diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCSessionInitiatorFlowTest.java b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCSessionInitiatorFlowTest.java
index cd68a20..b61b752 100644
--- a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCSessionInitiatorFlowTest.java
+++ b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCSessionInitiatorFlowTest.java
@@ -38,6 +38,7 @@ import org.opensaml.messaging.decoder.MessageDecodingException;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.springframework.context.ApplicationContext;
 import org.springframework.core.io.ClassPathResource;
+import org.springframework.test.annotation.DirtiesContext;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.web.WebAppConfiguration;
 import org.springframework.webflow.engine.impl.FlowExecutionImpl;
@@ -79,6 +80,7 @@ import net.shibboleth.sp.profile.SPConstants;
         inheritInitializers = false
         )
 @WebAppConfiguration
+ at DirtiesContext(classMode  = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
 @SuppressWarnings({ "null" })
 /**
  * Unit tests for OIDC session initiator flow.
@@ -245,6 +247,75 @@ public class OIDCSessionInitiatorFlowTest extends AbstractSPFlowTest {
         validateOutputMessage(result);
     }
     
+    /**
+     * Load OP metadata that only supports symetric key signing of a request object. Exercises the client_secret
+     * credential resolvers.
+     * 
+     * @throws Exception on exception
+     */
+    @Test
+    public void testFlowWithState_WithRO_SymetricSigned() throws Exception {
+        setDefaultAuth();
+        
+        final var metadataFromFile = new ClassPathResource("metadata/openid-configuration-hs256-request-object.json");
+        final String json = new String(metadataFromFile.getInputStream().readAllBytes(), StandardCharsets.UTF_8);         
+        mockProviderMetadataEndpoint(OIDCProviderMetadata.parse(JSONObjectUtils.parse(json)));
+        
+        final DDF input = new DDF(null).structure();
+        input.addmember(RemotedHttpServletRequest.STRUCTURE_NAME).structure();        
+        input.addmember(InitiatorConstants.RESPONSE_URL).string(RESPONSE_URL);
+        input.addmember(SPConstants.TARGET).unsafe_string(RESOURCE_URL);     
+        input.addmember(SPConstants.STATE).string("state");
+        setApplicationRequest(TestConstants.APPLICATION_ID_REQUEST_OBJECT, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        
+        validateOutputMessage(result);
+    }
+    
+    @Test
+    public void testFlowWithState_WithRO_SignedAndEncrypted() throws Exception {
+        setDefaultAuth();
+        
+        final DDF input = new DDF(null).structure();
+        input.addmember(RemotedHttpServletRequest.STRUCTURE_NAME).structure();        
+        input.addmember(InitiatorConstants.RESPONSE_URL).string(RESPONSE_URL);
+        input.addmember(SPConstants.TARGET).unsafe_string(RESOURCE_URL);     
+        input.addmember(SPConstants.STATE).string("state");
+        setApplicationRequest(TestConstants.APPLICATION_ID_REQUEST_OBJECT_ENCRYPTED, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        
+        validateOutputMessage(result);
+    }
+    
+    @Test
+    public void testFlowWithState_WithRO_SignedAndKeyWrappedEncrypted() throws Exception {
+        setDefaultAuth();
+        
+        final var metadataFromFile = new ClassPathResource("metadata/openid-configuration-a128kw-request-object.json");
+        final String json = new String(metadataFromFile.getInputStream().readAllBytes(), StandardCharsets.UTF_8);         
+        mockProviderMetadataEndpoint(OIDCProviderMetadata.parse(JSONObjectUtils.parse(json)));
+        
+        final DDF input = new DDF(null).structure();
+        input.addmember(RemotedHttpServletRequest.STRUCTURE_NAME).structure();        
+        input.addmember(InitiatorConstants.RESPONSE_URL).string(RESPONSE_URL);
+        input.addmember(SPConstants.TARGET).unsafe_string(RESOURCE_URL);     
+        input.addmember(SPConstants.STATE).string("state");
+        setApplicationRequest(TestConstants.APPLICATION_ID_REQUEST_OBJECT_KWENCRYPTED, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        
+        validateOutputMessage(result);
+    }
+    
+    
     /**
      * Basic flow test with forceAuthn.
      * 
diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java
index 5b9e3ab..5f179e5 100644
--- a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java
+++ b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java
@@ -14,6 +14,7 @@
 
 package net.shibboleth.sp.oidc.flows;
 
+import java.io.IOException;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.Date;
@@ -331,6 +332,50 @@ public class OIDCTokenConsumerFlowTest extends AbstractOIDCTokenConsumerFlowTest
                 TestConstants.RESOURCE_URL);
     }
     
+    @Test
+    public void testSuccess_SymmetricSigned_DirectEncryptedIDToken_PlainUserInfo() throws Exception {
+    	
+    	final JWTClaimsSet claimsSet = TestHelper.createBasicUserInfoClaimsWithExtra(
+    			Map.of("sub","jdoe","iss", TestConstants.ISSUER, 
+    					"name", "John Doe", "aud", List.of(TestConstants.CLIENT_ID)), 
+    			Map.of("iat", Instant.now(), "exp", Instant.now().plusSeconds(3600)));
+        
+        final JWT encryptedIdToken = 
+                TestHelper.createJWT(claimsSet, JWSAlgorithm.HS256, JWEAlgorithm.DIR, 
+                		EncryptionMethod.A256GCM, clientSecretCredential.toSigningCredential(), 
+                		clientSecretCredential.toEncryptionCredential(JWEAlgorithm.DIR, EncryptionMethod.A256GCM), 
+                		"client_secret_credential");
+        
+        final OIDCTokenResponse tokenResponse = 
+        		new OIDCTokenResponse((new OIDCTokens(encryptedIdToken, 
+        				new BearerAccessToken("fake-access-token-value", 3600, null), 
+        				new RefreshToken("fake-refresh-token-value"))));
+        
+        mockOIDCEndpoints(tokenResponse, constructJSONUserInfoResponse());
+
+        final AuthenticationSuccessResponse response = 
+                TestHelper.buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
+                        TestConstants.STATE_TOKEN);         
+        final DDF input = buildRemotedQueryStringResponse(response);
+        
+        // Add cookies
+        input.addmember("http.headers.Cookie").unsafe_string(TestHelper.buildCookieHeader(
+                TestConstants.STATE_TOKEN, 
+                TestConstants.APPLICATION_ID_REQUEST_OBJECT,
+                TestHelper.buildAuthenticationState(null, false, null), true));
+        
+        setApplicationRequest(TestConstants.APPLICATION_ID_REQUEST_OBJECT, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(TestConstants.FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, TestConstants.FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        final DDF output = assertOutputMessageSuccess(result);
+        assert output != null;
+        System.out.println("test output: " + output.toString());
+        validateOutputMessage(result, CollectionSupport.setOf("sub","mail","displayName","eduPersonScopedAffiliation"),
+                TestConstants.RESOURCE_URL);
+    }
+    
     /**
      * Test successful flow with a signed and encrypted id_token and a plain user info response. The kid of the key 
      * used to encrypt the IDToken is in the Headers.
@@ -338,7 +383,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractOIDCTokenConsumerFlowTest
      * @throws IOException on error
      */
     @Test
-    public void testSuccess_SignedEncryptedIDToken_WithDecryptionKID_PlainUserInfo() throws Exception {
+    public void testSuccess_Signed_EncryptedIDToken_WithDecryptionKID_PlainUserInfo() throws Exception {
         
         mockOIDCEndpoints(constructSuccessfulTokenResponseSignedAndEncrypted(Instant.now().plusSeconds(3600), 
                 Instant.now(), null, null, rpEncryptionCredential.getKid()), constructJSONUserInfoResponse());
@@ -373,7 +418,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractOIDCTokenConsumerFlowTest
      * @throws IOException on error
      */
     @Test
-    public void testSuccess_SignedDirectEncryptedIDToken_WithDecryptionKID_PlainUserInfo() throws Exception {       
+    public void testSuccess_Signed_DirectEncryptedIDToken_WithDecryptionKID_PlainUserInfo() throws Exception {       
     	final JWTClaimsSet claimsSet = TestHelper.createBasicUserInfoClaimsWithExtra(
     			Map.of("sub","jdoe","iss", TestConstants.ISSUER, 
     					"name", "John Doe", "aud", List.of(TestConstants.CLIENT_ID)), 
@@ -415,6 +460,49 @@ public class OIDCTokenConsumerFlowTest extends AbstractOIDCTokenConsumerFlowTest
                 TestConstants.RESOURCE_URL);
     }
     
+    @Test
+    public void testSuccess_Signed_AESKeyWrappedEncryptedIDToken_WithDecryptionKID_PlainUserInfo() throws Exception {       
+    	final JWTClaimsSet claimsSet = TestHelper.createBasicUserInfoClaimsWithExtra(
+    			Map.of("sub","jdoe","iss", TestConstants.ISSUER, 
+    					"name", "John Doe", "aud", List.of(TestConstants.CLIENT_ID)), 
+    			Map.of("iat", Instant.now(), "exp", Instant.now().plusSeconds(3600)));
+        
+        final JWT encryptedIdToken = 
+                TestHelper.createJWT(claimsSet, JWSAlgorithm.RS256, JWEAlgorithm.A128KW, 
+                		EncryptionMethod.A256GCM, opSigningCredential, 
+                		clientSecretCredential.toEncryptionCredential(JWEAlgorithm.A128KW, EncryptionMethod.A256GCM), 
+                		"client_secret_credential");
+        
+        final OIDCTokenResponse tokenResponse = 
+        		new OIDCTokenResponse((new OIDCTokens(encryptedIdToken, 
+        				new BearerAccessToken("fake-access-token-value", 3600, null), 
+        				new RefreshToken("fake-refresh-token-value"))));
+        
+        mockOIDCEndpoints(tokenResponse, constructJSONUserInfoResponse());
+
+        final AuthenticationSuccessResponse response = 
+                TestHelper.buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
+                        TestConstants.STATE_TOKEN);         
+        final DDF input = buildRemotedQueryStringResponse(response);
+        
+        // Add cookies
+        input.addmember("http.headers.Cookie").unsafe_string(TestHelper.buildCookieHeader(
+                TestConstants.STATE_TOKEN, 
+                TestConstants.APPLICATION_ID_REQUEST_OBJECT,
+                TestHelper.buildAuthenticationState(null, false, null), true));
+        
+        setApplicationRequest(TestConstants.APPLICATION_ID_REQUEST_OBJECT, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(TestConstants.FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, TestConstants.FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        final DDF output = assertOutputMessageSuccess(result);
+        assert output != null;
+        System.out.println("test output: " + output.toString());
+        validateOutputMessage(result, CollectionSupport.setOf("sub","mail","displayName","eduPersonScopedAffiliation"),
+                TestConstants.RESOURCE_URL);
+    }
+    
     /**
      * Test failure flow with a signed and encrypted id_token and a plain user info response. The kid of the key 
      * used to encrypt the IDToken is not one that matches to a local key defined.
diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializer.java b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializer.java
index 132526f..b230438 100644
--- a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializer.java
+++ b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializer.java
@@ -54,8 +54,6 @@ public class TestSPOIDCEnvironmentApplicationContextInitializer extends TestSPEn
         mock.setProperty("sp.oidc.idtoken.validateAcrValue", "true");
         // Turn off the sealing state data for testing
         mock.setProperty("sp.stateToken.sealed", "false");
-        // Create a basic default client secret
-        mock.setProperty("sp.oidc.defaultClientSecret", "zIq2qS8w8k7qzShC0Z6j8l0mKmdEFdt02KDv2Qz4xXQ");
         // Turn on the replay cache for testing
         mock.setProperty("sp.stateToken.checkReplay", "true");
         mock.setProperty("idp.additionalProperties",
diff --git a/sp-oidc-conf-impl/src/test/resources/metadata/openid-configuration-a128kw-request-object.json b/sp-oidc-conf-impl/src/test/resources/metadata/openid-configuration-a128kw-request-object.json
new file mode 100644
index 0000000..8ccd715
--- /dev/null
+++ b/sp-oidc-conf-impl/src/test/resources/metadata/openid-configuration-a128kw-request-object.json
@@ -0,0 +1,62 @@
+{
+"issuer": "https://op.example.org",
+"authorization_endpoint": "https://op.example.org/auth",
+"token_endpoint": "https://oauth2.op.example.org/token",
+"userinfo_endpoint": "https://op.example.org/userinfo",
+"revocation_endpoint": "https://op.example.org/revoke",
+"jwks_uri": "https://op.example.org/keys",
+"request_parameter_supported" : true,
+"request_object_signing_alg_values_supported" : ["RS256"],
+"response_types_supported": [
+"code",
+"token",
+"id_token",
+"code token",
+"code id_token",
+"token id_token",
+"code token id_token",
+"none"
+],
+"request_object_encryption_enc_values_supported" : ["A128CBC-HS256"],
+"request_object_encryption_alg_values_supported" : ["A128KW"],
+"subject_types_supported": [
+"public"
+],
+"id_token_signing_alg_values_supported": [
+"RS256"
+],
+"scopes_supported": [
+"openid",
+"email",
+"profile"
+],
+"claims_parameter_supported" : true,
+"token_endpoint_auth_methods_supported": [
+"client_secret_post",
+"client_secret_basic"
+],
+"claims_supported": [
+"aud",
+"email",
+"email_verified",
+"exp",
+"family_name",
+"given_name",
+"iat",
+"iss",
+"locale",
+"name",
+"picture",
+"sub"
+],
+"code_challenge_methods_supported": [
+"plain",
+"S256"
+],
+"grant_types_supported": [
+"authorization_code",
+"refresh_token"
+],
+"end_session_endpoint":
+   "https://op.example.org/end_session"
+}
\ No newline at end of file
diff --git a/sp-oidc-conf-impl/src/test/resources/metadata/openid-configuration-hs256-request-object.json b/sp-oidc-conf-impl/src/test/resources/metadata/openid-configuration-hs256-request-object.json
new file mode 100644
index 0000000..be84606
--- /dev/null
+++ b/sp-oidc-conf-impl/src/test/resources/metadata/openid-configuration-hs256-request-object.json
@@ -0,0 +1,62 @@
+{
+"issuer": "https://op.example.org",
+"authorization_endpoint": "https://op.example.org/auth",
+"token_endpoint": "https://oauth2.op.example.org/token",
+"userinfo_endpoint": "https://op.example.org/userinfo",
+"revocation_endpoint": "https://op.example.org/revoke",
+"jwks_uri": "https://op.example.org/keys",
+"request_parameter_supported" : true,
+"request_object_signing_alg_values_supported" : ["HS256"],
+"response_types_supported": [
+"code",
+"token",
+"id_token",
+"code token",
+"code id_token",
+"token id_token",
+"code token id_token",
+"none"
+],
+"request_object_encryption_enc_values_supported" : ["A128CBC-HS256"],
+"request_object_encryption_alg_values_supported" : ["RSA-OAEP-256"],
+"subject_types_supported": [
+"public"
+],
+"id_token_signing_alg_values_supported": [
+"RS256"
+],
+"scopes_supported": [
+"openid",
+"email",
+"profile"
+],
+"claims_parameter_supported" : true,
+"token_endpoint_auth_methods_supported": [
+"client_secret_post",
+"client_secret_basic"
+],
+"claims_supported": [
+"aud",
+"email",
+"email_verified",
+"exp",
+"family_name",
+"given_name",
+"iat",
+"iss",
+"locale",
+"name",
+"picture",
+"sub"
+],
+"code_challenge_methods_supported": [
+"plain",
+"S256"
+],
+"grant_types_supported": [
+"authorization_code",
+"refresh_token"
+],
+"end_session_endpoint":
+   "https://op.example.org/end_session"
+}
\ No newline at end of file
diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/agents.xml b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/agents.xml
index 59a4a62..376677c 100644
--- a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/agents.xml
+++ b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/agents.xml
@@ -12,7 +12,7 @@
        default-init-method="initialize"
        default-destroy-method="destroy">
 
-        <!--
+    <!--
     This is a test agent config file allowing the testbed to be used to exercise agents
     by importing the necessary credentials. It's deliberately unused by any unit tests.
     -->
@@ -38,11 +38,6 @@
     <util:list id="shibboleth.sp.RelyingPartyOverrides">
         <!-- Your RelyingParty overrides here. -->
     </util:list>
-    
-     <util:list id="shibboleth.sp.DefaultProfileConfigurations">
-        <!-- Your profiles here. -->
-        <bean parent="OIDC.SSOs" />
-    </util:list>
 
 
     <!-- ============ Overridden Profile Configuration Defaults ============ -->
@@ -64,6 +59,5 @@
     <util:list id="shibboleth.sp.UnverifiedProfileConfigurations">
     </util:list>
     -->
-
     
 </beans>
diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/credentials.xml b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/credentials.xml
new file mode 100644
index 0000000..7a611fd
--- /dev/null
+++ b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/credentials.xml
@@ -0,0 +1,51 @@
+<?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">
+
+    <!--
+    Protocol plugins will typically auto-install "default" credentials for simple
+    deployments to use. When supporting multiple Agents, migrating systems with
+    existing keys, or simply where required, this file provides a built-in location
+    to define additional keys, certificates, secrets, etc.
+    
+    Refer to protocol-specific documentation for how to use this file.
+    -->
+    
+    <!--  
+        Test credentials that need to be the same as those loaded by the flow tests. These are the same whatever
+        the agent and OP used by the tests. 
+    -->
+   <bean id="TestCredentialResolver.Static" parent="shibboleth.sp.oidc.CredentialResolver.Static">
+        <constructor-arg>
+            <bean id="oidc.DefaultCredentials" class="org.springframework.beans.factory.config.ListFactoryBean" lazy-init="true">
+                <property name="sourceList">
+                    <list>
+                         <bean id="shibboleth.oidc.DefaultRSSigningCredential" 
+                            parent="shibboleth.oidc.JWKCredential"
+                            p:resource="%{idp.home}/credentials/sp/sp-signing-rs.jwk"/> 
+                        
+                        <bean id="shibboleth.oidc.DefaultESSigningCredential" 
+		                    parent="shibboleth.oidc.JWKCredential"
+		                    p:resource="%{idp.home}/credentials/sp/sp-signing-es.jwk" />
+
+		                <bean id="shibboleth.oidc.DefaultRSAEncryptionCredential" 
+		                    parent="shibboleth.oidc.JWKCredential"
+		                    p:resource="%{idp.home}/credentials/sp/sp-encryption-rs.jwk"/>   
+                    </list>
+                </property>
+            </bean>
+        </constructor-arg>
+    </bean> 
+    
+    
+</beans>
diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-test-agents.xml b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-test-agents.xml
index f0d8708..6c6cbbf 100644
--- a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-test-agents.xml
+++ b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-test-agents.xml
@@ -38,6 +38,14 @@
                 <bean p:id="test-oidc-application-with-ro" parent="shibboleth.sp.Application"
                     p:authenticatingAuthority="https://op.example.org"
                     p:profileConfigurations-ref="test.RequestObjectProfileConfigurations"/>
+                    
+                <bean p:id="test-oidc-application-with-encrypted-ro" parent="shibboleth.sp.Application"
+                    p:authenticatingAuthority="https://op.example.org"
+                    p:profileConfigurations-ref="test.RequestObjectProfileConfigurations.Encrypted"/>
+                    
+                <bean p:id="test-oidc-application-with-kwencrypted-ro" parent="shibboleth.sp.Application"
+                    p:authenticatingAuthority="https://op.example.org"
+                    p:profileConfigurations-ref="test.RequestObjectProfileConfigurations.KW.Encrypted"/>
                  
                  <!-- Use private_key_jwt client authentication -->
                  <bean p:id="test-oidc-application-with-ro-private-key-jwt" parent="shibboleth.sp.Application"
@@ -61,8 +69,7 @@
 
     <!-- All test profile configurations use the same default secret no matter what the OP -->
     <util:list id="test.ProfileConfigurations">
-        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig" p:useRequestObject="false">
-        </bean>
+        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig" p:useRequestObject="false"/>
     </util:list>
     
     <util:list id="test.logout.WithPostLogoutRedirectURI">
@@ -70,34 +77,43 @@
     </util:list>
     
     <util:list id="test.WithPKCE">
-        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig" p:forcePKCE="true">
-        </bean>
+        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig" p:forcePKCE="true"/>
     </util:list>
     
     <util:list id="test.WithPostResponseMode">
-        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig" p:responseMode="form_post">
-        </bean>
+        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig" p:responseMode="form_post"/>
     </util:list>
     
     <util:list id="test.RequestObjectProfileConfigurations">
-        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig" p:useRequestObject="true" p:signRequestObject="true" p:encryptRequestObject="false">
-        </bean>
+        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig" p:useRequestObject="true" 
+        p:signRequestObject="true" p:encryptRequestObject="false"/>
+    </util:list>
+    
+    <util:list id="test.RequestObjectProfileConfigurations.Encrypted">
+        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig" p:useRequestObject="true" 
+        p:signRequestObject="true" p:encryptRequestObject="true"/>
+    </util:list>
+    
+    <util:list id="test.RequestObjectProfileConfigurations.KW.Encrypted">
+        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig.KeyWrapped" p:useRequestObject="true" 
+        p:signRequestObject="true" p:encryptRequestObject="true"/>
     </util:list>
     
     <util:list id="test.RequestObjectProfileConfigurations.PrivateKeyJWT">
-        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig" p:useRequestObject="true" p:signRequestObject="true" p:encryptRequestObject="false"
-            p:tokenEndpointAuthMethod="private_key_jwt">
-        </bean>
+        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig" p:useRequestObject="true" 
+        p:signRequestObject="true" p:encryptRequestObject="false"
+            p:tokenEndpointAuthMethod="private_key_jwt"/>
     </util:list>
     
     <util:list id="test.RequestObjectProfileConfigurations.ClientSecretJWT">
-        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig" p:useRequestObject="true" p:signRequestObject="true" p:encryptRequestObject="false"
-            p:tokenEndpointAuthMethod="client_secret_jwt">
-        </bean>
+        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig" p:useRequestObject="true" 
+        p:signRequestObject="true" p:encryptRequestObject="false"
+            p:tokenEndpointAuthMethod="client_secret_jwt"/>
     </util:list>
     
     <util:list id="test.RequestedClaimsProfileConfigurations">
-        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig" p:useRequestObject="true" p:signRequestObject="true" p:encryptRequestObject="false">
+        <bean parent="OIDC.SSO" p:securityConfiguration-ref="testSecConfig" p:useRequestObject="true" 
+        p:signRequestObject="true" p:encryptRequestObject="false">
             <property name="requestedClaimsLookupStrategy">
                 <bean id="basicRequestedClaims" class="net.shibboleth.sp.oidc.functions.RequestedClaimsExampleFunction"/>
             </property>      
@@ -108,39 +124,96 @@
         Overrides the default security configuration to provide an encryption credential resolver
         that loads JWKs from a local file instead of retrieving them from the issuer’s JWKS URI.
         This simplifies testing by avoiding reliance on an external JWKS endpoint. We also constrain
-        the algorithms used, to simplify baseline tests.
+        the algorithms used for encryption and signing, to simplify baseline tests.
      -->
-    <bean id="testSecConfig" parent="shibboleth.oidc.DefaultSecurityConfiguration">
-      <property name="jwtEncryptionConfiguration">
-        <bean id="dummy.oidc.EncryptionConfiguration" parent="dummy.oidc.BasicEncryptionConfiguration"
-            p:contentEncryptionKeyCredentialResolver-ref="defaultOIDCContentEncryptionKeyCredentialResolver">
-            <property name="keyTransportEncryptionAlgorithms">
-                <list>
-                    <util:constant
-                        static-field="net.shibboleth.oidc.jwa.support.KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256" />
-                </list>
-            </property>
-            <property name="dataEncryptionAlgorithms">
-                <list>
-                    <util:constant
-                        static-field="net.shibboleth.oidc.jwa.support.EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256" />
-                </list>
-            </property>
-            <property name="KEKCredentialResolver">
-                <bean id="defaultOIDCKeyEncryptionCredentialResolver"
-                    class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
-                    <constructor-arg>
-                        <list>
-                            <bean id="ProviderMetadataFileBasedCredentialResolver"
-                                class="net.shibboleth.sp.oidc.metadata.ProviderMetadataFileBasedCredentialResolver"
-                                c:jwkSet="classpath:/net/shibboleth/idp/module/credentials/op/global-provider-test-jwks.json" />
-                        </list>
-                    </constructor-arg>
-                </bean>
-            </property>
-        </bean>
-      </property>
-    </bean>
+     
+     <bean id="testSecConfig.Base"
+      parent="shibboleth.oidc.DefaultSecurityConfiguration"
+      abstract="true"/>
+
+	<bean id="testSecConfig"
+	      parent="testSecConfig.Base"
+	      p:jwtEncryptionConfiguration-ref="testEncryptionConfiguration.RSA"/>
+	
+	<bean id="testSecConfig.KeyWrapped"
+	      parent="testSecConfig.Base"
+	      p:jwtEncryptionConfiguration-ref="testEncryptionConfiguration.A128KW"/>
+     
+    
+    <!-- Shared KEK resolver -->
+	<bean id="testOIDCKeyEncryptionCredentialResolver"
+	      class="net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver">
+	    <constructor-arg>
+	        <list>
+	
+	            <bean class="net.shibboleth.sp.oidc.metadata.ProviderMetadataFileBasedCredentialResolver"
+	                  c:jwkSet="classpath:/net/shibboleth/idp/module/credentials/op/global-provider-test-jwks.json"/>
+	
+	            <bean parent="shibboleth.sp.oidc.LocalCredentialResolver"
+	                  c:criteriaPassthrough="true">
+	                <constructor-arg name="criteria">
+	                    <set>
+	                        <bean class="org.opensaml.security.criteria.UsageCriterion">
+	                            <constructor-arg>
+	                                <util:constant
+	                                    static-field="org.opensaml.security.credential.UsageType.ENCRYPTION"/>
+	                            </constructor-arg>
+	                        </bean>
+	
+	                        <bean class="org.opensaml.saml.criterion.ProtocolCriterion">
+	                            <constructor-arg>
+	                                <util:constant
+	                                    static-field="net.shibboleth.oidc.saml.xmlobject.Constants.OIDC_PROTOCOL_URI"/>
+	                            </constructor-arg>
+	                        </bean>
+	                    </set>
+	                </constructor-arg>
+	            </bean>
+	
+	        </list>
+	    </constructor-arg>
+	</bean>
+	
+	<!-- Common encryption settings -->
+	<bean id="testEncryptionConfiguration"
+	      parent="dummy.oidc.BasicEncryptionConfiguration"
+	      abstract="true"
+	      p:contentEncryptionKeyCredentialResolver-ref="defaultOIDCContentEncryptionKeyCredentialResolver">
+	
+	    <property name="dataEncryptionAlgorithms">
+	        <list>
+	            <util:constant
+	                static-field="net.shibboleth.oidc.jwa.support.EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256"/>
+	        </list>
+	    </property>
+	
+	    <property name="KEKCredentialResolver"
+	              ref="testOIDCKeyEncryptionCredentialResolver"/>
+	</bean>
+	
+	<!-- RSA-OAEP -->
+	<bean id="testEncryptionConfiguration.RSA"
+	      parent="testEncryptionConfiguration">
+	    <property name="keyTransportEncryptionAlgorithms">
+	        <list>
+	            <util:constant
+	                static-field="net.shibboleth.oidc.jwa.support.KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256"/>
+	        </list>
+	    </property>
+	</bean>
+	
+	<!-- AES Key Wrap -->
+	<bean id="testEncryptionConfiguration.A128KW"
+	      parent="testEncryptionConfiguration">
+	    <property name="keyTransportEncryptionAlgorithms">
+	        <list>
+	            <util:constant
+	                static-field="net.shibboleth.oidc.jwa.support.KeyManagementConstants.ALGO_ID_ALG_AES_128_KW"/>
+	        </list>
+	    </property>
+	</bean>
+	
+
 
 
     <!-- ============ RelyingParty Overrides ============ -->
@@ -152,17 +225,7 @@
     -->
 
     <util:list id="shibboleth.sp.RelyingPartyOverrides">
-       <!--<bean id="OpenID" parent="RelyingPartyByName" c:relyingPartyIds="https://op.example.org">
-            <property name="profileConfigurations">
-              <list>
-                  <bean parent="OIDC.SSO" p:useRequestObject="false">
-                    <property name="clientCredential">
-                            <bean parent="shibboleth.oidc.ClientSecretCredential" p:secret="another-secret-cooool"/>
-                    </property> 
-                  </bean>
-              </list>
-           </property>
-         </bean> -->
+
     </util:list>
 
 
diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-test.properties b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-test.properties
index b2b3e32..2e769df 100644
--- a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-test.properties
+++ b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-test.properties
@@ -1,16 +1,4 @@
 # OIDC-specific RP test settings
 
-## TODO check where these are being used
-
-sp.oidc.signing.rs.key = %{idp.home}/credentials/sp/sp-signing-rs.jwk
-sp.oidc.signing.es.key = %{idp.home}/credentials/sp/sp-signing-es.jwk
-sp.oidc.encryption.rsa.key = %{idp.home}/credentials/sp/sp-encryption-rsa.jwk
-
-#sp.oidc.encryption.key.2 = %{idp.home}/credentials/sp/sp-encryption-old.key
-#sp.oidc.encryption.cert.2 = %{idp.home}/credentials/sp/sp-encryption-old.crt
-
-#sp.oidc.extractStandardAttributes = true
-
-#sp.oidc.acceptUnfilteredAttributes = true
 
 
diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/credentials/sp/sp-encryption-rsa.jwk b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/credentials/sp/sp-encryption-rs.jwk
similarity index 100%
rename from sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/credentials/sp/sp-encryption-rsa.jwk
rename to sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/credentials/sp/sp-encryption-rs.jwk
diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/credentials/sp/sp-encryption.crt b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/credentials/sp/sp-encryption.crt
deleted file mode 100644
index f0e542e..0000000
--- a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/credentials/sp/sp-encryption.crt
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDCTCCAfGgAwIBAgIJAN5X7xMzDu64MA0GCSqGSIb3DQEBBQUAMBkxFzAVBgNV
-BAMTDnNwLmV4YW1wbGUub3JnMB4XDTEzMDUyNDIxMzU0NloXDTIzMDUyMjIxMzU0
-NlowGTEXMBUGA1UEAxMOc3AuZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUA
-A4IBDwAwggEKAoIBAQDAVA9a1ip1mvd5rQJe9xhL6TXvIHxPm+FLxabvQJA48sJh
-XsXmLAsyGzSm/HviqdkdR3VxyFhzLoj03TqqPP8/IoTFA2Lp+q9PnaeEHKaYeJGH
-g4RozQ0UKhiuyppWptO2Syls302haIaWWXz+p3iOgjuZKg/ESs1udGEJvAC/OUVu
-Eo08b6BuySxO/KsX9kWYoCF6vm+tN1kpOrMTceCqcJlRDyFzxUo4JTaku/81k88v
-iF+5/b0p7McB3IGqCFHR6MhZol880Dgxeze3o6/hmvXwGbJgOeWc46jgKareWwYn
-CSQ7/08TDvX0RhCca39IJXqSP/Wv4QOkALTdobQhAgMBAAGjVDBSMDEGA1UdEQQq
-MCiCDnNwLmV4YW1wbGUub3JnhhZodHRwczovL3NwLmV4YW1wbGUub3JnMB0GA1Ud
-DgQWBBQ1FR+wdqXqtICqb7G0fms7ELdt1zANBgkqhkiG9w0BAQUFAAOCAQEAoW/5
-uKnQMuQJfEx11uVCItAvRNmjWSmGFXpQ3rown3XbRkdX78PztcIdgwwtNZoTlMmA
-122Kdb8ViXw2tsZaB1iOaBiavMtLgdxbg9j6B6UmQrzERQm+zq5rBAzSTh6VRyDP
-kCMYnlX4+PyjWXGVADI65cRBbFBnED6By0NhJesDfaH8VpY19LN9AYpXVJRv4/Dc
-zlnQds4vPKzOaJGvt01Ni9jqZtcUDzKt9UdmDATbukoStvCKTwcr4gAPvfCfNZgo
-CUaW4GEmfyDkCahd5qNlWw0++Z2kNoSRyPA1PTJIkZhagv+VbO85ua2SDJWVgF4I
-h+XNZ5oAql/cjrwDWw==
------END CERTIFICATE-----
diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/credentials/sp/sp-encryption.key b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/credentials/sp/sp-encryption.key
deleted file mode 100644
index ea775a2..0000000
--- a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/credentials/sp/sp-encryption.key
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEpQIBAAKCAQEAwFQPWtYqdZr3ea0CXvcYS+k17yB8T5vhS8Wm70CQOPLCYV7F
-5iwLMhs0pvx74qnZHUd1cchYcy6I9N06qjz/PyKExQNi6fqvT52nhBymmHiRh4OE
-aM0NFCoYrsqaVqbTtkspbN9NoWiGlll8/qd4joI7mSoPxErNbnRhCbwAvzlFbhKN
-PG+gbsksTvyrF/ZFmKAher5vrTdZKTqzE3HgqnCZUQ8hc8VKOCU2pLv/NZPPL4hf
-uf29KezHAdyBqghR0ejIWaJfPNA4MXs3t6Ov4Zr18BmyYDnlnOOo4Cmq3lsGJwkk
-O/9PEw719EYQnGt/SCV6kj/1r+EDpAC03aG0IQIDAQABAoIBAQCItrg7f+Wqt7+W
-9I6s5sSUoXA/TT5QcKQT8qoZhDGA6b+W64GNfl1u/rvMv8lodAfrsjIf8aQ+LCQa
-u2/KN5XRUT5DtYaIgDRCSfUcCim6VGpOhASJYskbPllRVp+jXO89s+v8gjdytxSE
-p5QciPs/gf/PfoC6rdmd3IhmHQgC5PxCu/3GvlB2n7ec/twPcaBG2BY95jLTJg4C
-3Xfiwl0qK2rNX1axiywEYwGyLDl4eco4t6MYLXMmB/QyDETRLx/6U6JsfgoY/2+W
-aWq6NSDDs5z5DzI8vAef9sxUdw1iZ4OfeyKPVJ5FaI5OlzDq2kWZ8p9p4B3w/pe3
-fPVM/cABAoGBAPPP1GiooBHxt2HwSjUeokGibZv9yv8ut8V6odxJKFCJHAiSVkFH
-soR8Q/8Nd8oeNrRe41qnRffmgSoBlnQUxOikDyO2gqnE45Keb0I8nlGN6UQQSDct
-Pc4GiEhQU3cPWgRmQYcxMxIZTL5BZRnoqE70ka/zr7pmdwmUCffVTv8BAoGBAMnx
-X3lafsU0aSnAfMF8zCGiHlvpUGoZ7l1wcCMpUfiaLYh3AkT15CoXBIo4rupXblKq
-Y0EPVG6KbzeGxZ07HsMr9kIQOSjRehnxHobGqzsLCMpNZFQEx45RIwy1fkpw3bHH
-/wyGyQrLDkisMwgqbjgjs6f/Ls1ubUJQH0nrR9UhAoGBAIC6GGHxtiIDR1l0bxnA
-xNZ2lfyocQdueSTQ7KjMD3cGDUERUm1BF6YPni8Jw1d5wFRXTR7N9ok+sK+nCvLb
-DGPwSvYGJNLR233MHN4R910D2kdPeXF4d4tRs57KmmbgOhzciHiCp35E0VoaWPDn
-AAY/5rPJq9x0wSDTgrfRbdYBAoGBAI3WgheLT0UV1dNkhnukOwBYjPhDfJ65TenF
-Qo3/xLpXGCokZKypBLPnbOHkhD7GYA+h64rTyJrHkuwuNfiEgMvWweWO3Ybk8tzr
-tWQ2gCg9TNRC3XgBz2QPaw3PIEAK3Du1XFehB+4TxxmWEVXn73hw4wvZOTdDG2yb
-OA7EObThAoGASjDc6zKQO+/7pwulathFMfnleETgKni4bq0MQ3ad6TRZdj7NwvbD
-w6ccGtC5qLQfqPZgPKZQavxXD3ENL/DPj9cWdRFEJUIkEQfMalyek1xogyXY09RZ
-kcF18N167B5zly0rALGo7godgiahjcC5S8l/T1zJeYESwkMG6o7KHw8=
------END RSA PRIVATE KEY-----
diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/sp/oidc-test-beans.xml b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/sp/oidc-test-beans.xml
index 4c5b857..b1b976d 100644
--- a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/sp/oidc-test-beans.xml
+++ b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/sp/oidc-test-beans.xml
@@ -31,15 +31,18 @@
     <bean id="shibboleth.oidc.ClientSecretCredential" abstract="true"
         class="net.shibboleth.oidc.security.credential.BasicClientSecretCredentialFactoryBean" />   
 
-    <!-- These need to be the same as those resolvable by the security configuration used -->
+    <!-- 
+        These need to be the same as those resolvable by the security configuration used 
+    -->
     <bean id="dummy.op.signing.Credential" parent="shibboleth.oidc.JWKCredential"
         p:resource="%{idp.home}/credentials/op/op-signing-rsa.jwk" p:throwIfNull="false" />
         
     <bean id="dummy.sp.encryption.Credential" parent="shibboleth.oidc.JWKCredential"
-        p:resource="%{idp.home}/credentials/sp/sp-encryption-rsa.jwk" p:throwIfNull="false" />
-        
+        p:resource="%{idp.home}/credentials/sp/sp-encryption-rs.jwk" p:throwIfNull="false" />
+    
+    <!-- This MUST be the same as the client secret accessible by the resolvers e.g. an appropriate one in client_secrets.txt -->    
     <bean id="dummy.sp.DefaultClientSecretCredential" parent="shibboleth.oidc.ClientSecretCredential"
-        p:secret="%{sp.oidc.defaultClientSecret:#{null}}"/>
+        p:secret="zIq2qS8w8k7qzShC0Z6j8l0mKmdEFdt02KDv2Qz4xXQ"/>
 
     <!-- Mockito mock for HttpClient -->
     <bean id="Mock.HttpClient"
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/jose/config/impl/ClientSecretCredentialResolver.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/jose/config/impl/ClientSecretCredentialResolver.java
index a590f19..ec563bf 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/jose/config/impl/ClientSecretCredentialResolver.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/jose/config/impl/ClientSecretCredentialResolver.java
@@ -22,7 +22,6 @@ import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.profile.criterion.ProfileRequestContextCriterion;
 import org.opensaml.security.credential.Credential;
 import org.opensaml.security.credential.CredentialResolver;
-import org.opensaml.security.credential.impl.AbstractCredentialResolver;
 import org.slf4j.Logger;
 
 import jakarta.servlet.ServletRequest;
@@ -52,8 +51,7 @@ import net.shibboleth.sp.criteria.ApplicationIDCriterion;
  * this is essential so secrets are not sent to the wrong Provider. It is left to the credential resolver service to 
  * ensure appropriate credentials are returned.</p>
  */
-public class ClientSecretCredentialResolver extends AbstractCredentialResolver 
-																		implements JOSEObjectCredentialResolver {
+public class ClientSecretCredentialResolver implements JOSEObjectCredentialResolver {
     
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(ClientSecretCredentialResolver.class);
@@ -77,6 +75,16 @@ public class ClientSecretCredentialResolver extends AbstractCredentialResolver
     	credentialResolver = Constraint.isNotNull(resolver, "CredentialResolver cannot be null");
     	servletRequestSupplier = Constraint.isNotNull(requestSupplier, "ServletRequest supplier cannot be null");
     }
+    
+    /** {@inheritDoc} */
+    @Override
+	@Nullable public Credential resolveSingle(@Nullable final CriteriaSet criteriaSet) throws ResolverException {
+        final Iterable<Credential> creds = resolve(criteriaSet);
+        if (creds.iterator().hasNext()) {
+            return creds.iterator().next();
+        }
+        return null;
+    }
  
     
     /** {@inheritDoc} */
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/jose/config/impl/StaticFilteringCriterionCredentialResolver.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/jose/config/impl/StaticFilteringCriterionCredentialResolver.java
index e293dc4..72281ea 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/jose/config/impl/StaticFilteringCriterionCredentialResolver.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/jose/config/impl/StaticFilteringCriterionCredentialResolver.java
@@ -69,7 +69,7 @@ public class StaticFilteringCriterionCredentialResolver extends AbstractCriteria
      * If the {@link CriteriaSet} from the caller should be copied into the {@link CriteriaSet} passed into the 
      * credential resolver. Defaults to false if not set, where only the staticCriteria are passed through.
      */
-    private final boolean enableCriteriaPassthrough;
+    private final boolean criteriaPassthrough;
     
     /**
      * Constructor.
@@ -89,7 +89,7 @@ public class StaticFilteringCriterionCredentialResolver extends AbstractCriteria
         } else {
             staticCriteria = CollectionSupport.emptySet();
         }
-        enableCriteriaPassthrough = passthrough;
+        criteriaPassthrough = passthrough;
     }
     
     /**
@@ -109,10 +109,14 @@ public class StaticFilteringCriterionCredentialResolver extends AbstractCriteria
     @Nonnull @NonnullElements public Iterable<Credential> resolveFromSource(
             @Nullable final CriteriaSet criteriaSet) throws ResolverException {
     	final CriteriaSet finalCriteria = new CriteriaSet();
-    	if (enableCriteriaPassthrough && criteriaSet != null) {
+    	if (criteriaPassthrough && criteriaSet != null) {
     		finalCriteria.addAll(criteriaSet);
     	}
-        finalCriteria.addAll(staticCriteria);
+    	// Add all that do not duplicate
+    	staticCriteria.stream()
+        	.filter(criterion -> !finalCriteria.contains(criterion))
+        	.forEach(finalCriteria::add);
+
         return credentialResolver.resolve(finalCriteria);
     }
     
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/jose/credential/impl/ClientSecretStorageServiceResolver.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/jose/credential/impl/ClientSecretStorageServiceResolver.java
index 971b826..ed74752 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/jose/credential/impl/ClientSecretStorageServiceResolver.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/jose/credential/impl/ClientSecretStorageServiceResolver.java
@@ -19,6 +19,7 @@ import com.nimbusds.jose.EncryptionMethod;
 import com.nimbusds.jose.JOSEException;
 import com.nimbusds.jose.JWEAlgorithm;
 
+import net.shibboleth.oidc.security.CredentialConversionUtil;
 import net.shibboleth.oidc.security.credential.ClientSecretCredential;
 import net.shibboleth.oidc.security.credential.DefaultClientSecretCredential;
 import net.shibboleth.oidc.security.credential.impl.DataEncryptionAlgorithmCriterion;
@@ -96,7 +97,7 @@ public class ClientSecretStorageServiceResolver extends AbstractStorageServiceCr
 			final CriteriaSet criteria, final String storageContext) throws ResolverException {
 		
 		if (!verifyCriteria(criteria)) {
-			log.debug("Unsuitable criteria for client secret credential resolution");
+			log.debug("CredentialResolver {}: Unsuitable criteria for client secret credential resolution", getId());
 			return CollectionSupport.emptyList();
 		}
 		
@@ -120,13 +121,17 @@ public class ClientSecretStorageServiceResolver extends AbstractStorageServiceCr
         
         log.debug("CredentialResolver {}: Resolved data for client secret, constructing credential", getId());
         
+        //TODO probably don't need to use a client secret credential here, we could inline the toSigning and Encryption logic
         final Credential derivedCredential = 
         		deriveClientSecretCredential(new DefaultClientSecretCredential(secretData.getValue()), criteria);		
 		if (derivedCredential == null) {
-			log.debug("Unable to derive a suitable credential from client secret");
+			log.debug("CredentialResolver {}: Unable to derive a suitable credential from client secret", getId());
 			return CollectionSupport.emptyList();
 		}
-		log.debug("CredentialResolver {}: Resolved client secret", getId());
+		if (log.isDebugEnabled()) {
+			final String kid = CredentialConversionUtil.resolveKid(derivedCredential);
+			log.debug("CredentialResolver {}: Resolved client secret '[{}]'", getId(), kid != null ? kid : "unknown");
+		}
 		return CollectionSupport.listOf(derivedCredential);
 		
 	}
@@ -141,13 +146,14 @@ public class ClientSecretStorageServiceResolver extends AbstractStorageServiceCr
     private boolean verifyCriteria(@Nullable final CriteriaSet criteriaSet) {
     	
     	if (criteriaSet == null) {
-    		log.trace("No criteria set found, can not process client secrets");
+    		log.trace("CredentialResolver {}: No criteria set found, can not process client secrets", getId());
     		return false;
     	}
     	
 		final UsageCriterion usageTypeCriterion = criteriaSet.get(UsageCriterion.class);
         if (usageTypeCriterion == null) {
-        	log.trace("No usage type criterion supplied, unable to derive client_secret credential");
+        	log.trace("CredentialResolver {}: No usage type criterion supplied, unable to derive client_secret "
+        			+ "credential", getId());
         	return false;
         }
         
@@ -156,14 +162,16 @@ public class ClientSecretStorageServiceResolver extends AbstractStorageServiceCr
 	        final KeyManagmentAlgorithmCriterion alg = criteriaSet.get(KeyManagmentAlgorithmCriterion.class);
 	        if (alg == null) {
 	        	log.trace(
-	                    "Credential criteria set did not contain an instance of KeyManagmentAlgorithmCriterion");
+	                    "CredentialResolver {}: Credential criteria set did not contain an instance of "
+	                    + "KeyManagmentAlgorithmCriterion", getId());
 	        	return false;
 	        }
 	        // The encryption method is only relevant to key derivation for the Direct Encryption mode
 	        final DataEncryptionAlgorithmCriterion enc = criteriaSet.get(DataEncryptionAlgorithmCriterion.class);
 	        if (enc == null) {
 	        	log.trace(
-	                    "Credential criteria set did not contain an instance of DataEncryptionAlgorithmCriterion");
+	                    "CredentialResolver {}: Credential criteria set did not contain an instance of "
+	                    + "DataEncryptionAlgorithmCriterion", getId());
 	        	return false;
 	        }
         }
@@ -194,7 +202,7 @@ public class ClientSecretStorageServiceResolver extends AbstractStorageServiceCr
         if (usageType == UsageType.SIGNING) {
             // Create a signing credential
             final Credential signingCred = secretCred.toSigningCredential();
-            log.debug("Derived signing credential '{}'", signingCred.getKeyNames());            
+            log.debug("CredentialResolver {}: Derived signing credential '{}'",getId(), signingCred.getKeyNames());            
             return signingCred;
             
         } else if (usageType == UsageType.ENCRYPTION) {
@@ -212,17 +220,17 @@ public class ClientSecretStorageServiceResolver extends AbstractStorageServiceCr
                     final Credential derivedCred = secretCred.toEncryptionCredential(
                             jweAlgorithm, encryptionMethod);
                     
-                    log.debug("Derived encryption credential '{}' from 'alg={}' and 'enc={}'", derivedCred.getKeyNames()
-                            ,alg.getAlgorithm(), enc.getEncAlgorithm());
+                    log.debug("CredentialResolver {}: Derived encryption credential '{}' from 'alg={}' and 'enc={}'", 
+                    		getId(), derivedCred.getKeyNames(),alg.getAlgorithm(), enc.getEncAlgorithm());
                     return derivedCred;
                     
                 } catch (final JOSEException e) {
-                    log.trace("Unable to derive symmetric encryption key from client_secret using 'alg={}' "
-                    		+ "and 'enc={}'", alg.getAlgorithm(), enc.getEncAlgorithm());      
+                    log.trace("CredentialResolver {}: Unable to derive symmetric encryption key from client_secret "
+                    		+ "using 'alg={}' and 'enc={}'",getId(), alg.getAlgorithm(), enc.getEncAlgorithm());      
                     throw new ResolverException("Unable to create encryption key from client_secret", e);
                 }                
             } else {    
-                log.trace("Asymmetric key requested, client_secret not appropriate");
+                log.trace("CredentialResolver {}: Asymmetric key requested, client_secret not appropriate", getId());
                 return null;
             }
         } else { 

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


More information about the commits mailing list