[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