[java-idp-plugin-vci] branch dev/PROFILES_AND_DPOP_AND_NONCE updated: Reworking/nonce endpoint

Codeberg noreply at shibboleth.net
Wed Nov 19 14:14:35 UTC 2025


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

codeberg pushed a commit to branch dev/PROFILES_AND_DPOP_AND_NONCE
in repository java-idp-plugin-vci.

View the commit online:
https://codeberg.org/Shibboleth/java-idp-plugin-vci/commit/f30ff1ecbf5043a4a1ea44ecd11cec7139d9b17e

The following commit(s) were added to refs/heads/dev/PROFILES_AND_DPOP_AND_NONCE by this push:
     new f30ff1e  Reworking/nonce endpoint
f30ff1e is described below

commit f30ff1ecbf5043a4a1ea44ecd11cec7139d9b17e
Author: jlauros <janne.lauros at csc.fi>
AuthorDate: Wed Nov 19 16:14:17 2025 +0200

    Reworking/nonce endpoint
---
 .../config/impl/DefaultOpenIDVCIConfiguration.java | 11 ----
 .../impl/FormOutboundNonceResponseMessage.java     | 77 ++++++++++++++++++++--
 .../META-INF/net.shibboleth.idp/postconfig.xml     | 36 +++++-----
 .../idp/flows/openid/vci/nonce/nonce-flow.xml      |  1 -
 .../idp/service/relying-party}/postconfig.xml      | 36 +++++-----
 .../impl/FormOutboundTokenResponseMessageTest.java |  4 +-
 6 files changed, 112 insertions(+), 53 deletions(-)

diff --git a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/impl/DefaultOpenIDVCIConfiguration.java b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/impl/DefaultOpenIDVCIConfiguration.java
deleted file mode 100644
index 43824d6..0000000
--- a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/config/impl/DefaultOpenIDVCIConfiguration.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.geant.shibboleth.plugin.openidvci.config.impl;
-
-import org.geant.shibboleth.plugin.openidvci.config.OpenIDVCIConfiguration;
-
-public class DefaultOpenIDVCIConfiguration extends AbstractOpenIDVCIConfiguration{
-
-    public DefaultOpenIDVCIConfiguration() {
-        super(OpenIDVCIConfiguration.PROFILE_ID);
-    }
-
-}
diff --git a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundNonceResponseMessage.java b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundNonceResponseMessage.java
index b79cd2b..5d17c06 100644
--- a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundNonceResponseMessage.java
+++ b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundNonceResponseMessage.java
@@ -16,31 +16,94 @@
 
 package org.geant.shibboleth.plugin.openidvci.profile.impl;
 
+import java.util.function.Function;
+
 import javax.annotation.Nonnull;
 
-import org.geant.shibboleth.plugin.openidvci.messaging.impl.CredentialOfferSuccessResponse;
 import org.geant.shibboleth.plugin.openidvci.messaging.impl.NonceSuccessResponse;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.ActionSupport;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
 import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.idp.profile.IdPEventIds;
+import net.shibboleth.oidc.profile.oauth2.config.OAuth2DPoPProofValidatingProfileConfiguration;
+import net.shibboleth.profile.config.ProfileConfiguration;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.logic.Constraint;
 
 /**
- * Action forming {@link CredentialOfferSuccessResponse}
+ * Action forming {@link NonceSuccessResponse}
  */
 public class FormOutboundNonceResponseMessage extends AbstractProfileAction {
 
     /** Class logger. */
     @Nonnull
     private Logger log = LoggerFactory.getLogger(FormOutboundNonceResponseMessage.class);
-  
+
+    @NonnullAfterInit
+    private Function<ProfileRequestContext, String> generator;
+
+    /**
+     * Strategy used to locate the {@link RelyingPartyContext} associated with a
+     * given {@link ProfileRequestContext}.
+     */
+    @Nonnull
+    private Function<ProfileRequestContext, RelyingPartyContext> relyingPartyContextLookupStrategy;
+
+    /** Relying party context. */
+    private RelyingPartyContext rpCtx;
+
+    public FormOutboundNonceResponseMessage() {
+        relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
+    }
+
+    /**
+     * Set the strategy used to locate the {@link RelyingPartyContext} associated
+     * with a given {@link ProfileRequestContext}.
+     * 
+     * @param strategy strategy used to locate the {@link RelyingPartyContext}
+     *                 associated with a given {@link ProfileRequestContext}
+     */
+    public void setRelyingPartyContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, RelyingPartyContext> strategy) {
+        checkSetterPreconditions();
+
+        relyingPartyContextLookupStrategy = Constraint.isNotNull(strategy,
+                "RelyingPartyContext lookup strategy cannot be null");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+        rpCtx = relyingPartyContextLookupStrategy.apply(profileRequestContext);
+        if (rpCtx == null) {
+            log.error("{} No relying party context associated with this profile request", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CTX);
+            return false;
+        }
+        final ProfileConfiguration pc = rpCtx.getProfileConfig();
+        if (pc instanceof OAuth2DPoPProofValidatingProfileConfiguration configuration) {
+            generator = configuration.getDpopProofNonceGenerator(profileRequestContext);
+        } else {
+            log.error("{} No OpenID VCI profile configuration associated with this profile request", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CTX);
+            return false;
+        }
+        if (generator == null) {
+            log.error("{} No nonce generator available for this profile request", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CTX);
+            return false;
+        }
+        return super.doPreExecute(profileRequestContext);
+    }
 
     /** {@inheritDoc} */
     @Override
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-        // TODO: code flow ignores 'issuer_state'. Authentication endpoint should check
-        // it. See Specification for guidance.
-        profileRequestContext.ensureOutboundMessageContext().setMessage(new NonceSuccessResponse("nonocenonocenocneocneocneocneonceocn"));
+        profileRequestContext.ensureOutboundMessageContext()
+                .setMessage(new NonceSuccessResponse(generator.apply(profileRequestContext)));
     }
 }
\ No newline at end of file
diff --git a/openid-vci-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/openid-vci-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 4f9a82c..bb0eba3 100644
--- a/openid-vci-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/openid-vci-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -17,7 +17,7 @@
         p:placeholderPrefix="%{" p:placeholderSuffix="}" />
 
     
-    <bean id="AbstractVCIProfile" abstract="true" 
+    <bean id="AbstractVCIProfile" abstract="true"
         p:issuer="#{getObject('shibboleth.oidc.issuer')}"
         p:preauthorizedCodeLifetime="%{openidvci.preauthorizedCode:PT10M}"
         p:preauthorizedCodeLength="%{openidvci.preauthorizedCode.defaultLength:0}"        
@@ -33,33 +33,37 @@
         p:dpopProofSignatureValidationConfiguration="#{getObject('DPoPSignatureValidationConfiguration')}"
         p:dpopProofNonceGenerator="#{getObject('DefaultOAuth2DPoPNonceGenerator')}" />
     
-    <!-- TB REMOVED -->    
-    <bean id="OpenID.VCI" parent="AbstractVCIProfile" lazy-init="true"
-        class="org.geant.shibboleth.plugin.openidvci.config.impl.DefaultOpenIDVCIConfiguration"
-        p:preauthorizedCodeLifetime="%{openidvci.preauthorizedCode:PT10M}"
-        p:preauthorizedCodeLength="%{openidvci.preauthorizedCode.defaultLength:0}"/>
         
     <bean id="OpenID.VCI.CredentialOffer" parent="AbstractVCIProfile" lazy-init="true"
         class="org.geant.shibboleth.plugin.openidvci.config.impl.DefaultOpenIDVCICredentialOfferConfiguration" />
-        
+
     <bean id="OpenID.VCI.Token" parent="AbstractVCIProfile" lazy-init="true"
         class="org.geant.shibboleth.plugin.openidvci.config.impl.DefaultOpenIDVCITokenConfiguration" />
-        
+
     <bean id="OpenID.VCI.Credentials" parent="AbstractVCIProfile" lazy-init="true"
         class="org.geant.shibboleth.plugin.openidvci.config.impl.DefaultOpenIDVCICredentialsConfiguration" />
-        
+
     <bean id="OpenID.VCI.Nonce" parent="AbstractVCIProfile" lazy-init="true"
-        class="org.geant.shibboleth.plugin.openidvci.config.impl.DefaultOpenIDVCINonceConfiguration" />
+        class="org.geant.shibboleth.plugin.openidvci.config.impl.DefaultOpenIDVCINonceConfiguration"
+        p:dpopProofNonceGenerator="#{getObject('DefaultOpenIDVCINonceGenerator')}" />
         
     <!-- Property-based definition of login flows for OAuth endpoints. -->
     <bean id="openidvci.PotentialFlows"
         class="org.springframework.beans.factory.config.ListFactoryBean"
         p:sourceList="#{getObject('shibboleth.AuthenticationFlowDescriptorManager').getComponents().?[id matches 'authn/(' + '%{openidvci.authn.flows:OAuth2Client}'.trim() + ')']}" />
-        
-    <bean id="openidvci.PublicClientValidator"
-        class="org.geant.shibboleth.plugin.openidvci.authn.impl.WalletCredentialValidator" />
-        
-    <bean id="openidvci.AuthzCodeManipulationStrategy"
-        class="org.geant.shibboleth.plugin.openidvci.profile.logic.AuthorizationCodeManipulationStrategy" />
+    
+    
+    <!-- We use special DPoP Nonce generator for Nonce endpoint. -->
+    <!-- The generator does not tie client id to nonce to accomodate unprotected Nonce endpoint. -->    
+    <bean id="DefaultOpenIDVCINonceGenerator" parent="DefaultOAuth2DPoPNonceGenerator" lazy-init="true"
+        p:relyingPartyIdLookupStrategy-ref="openidvci.RelyingPartyForNonce">
+    </bean>
+
+    <!-- Mock relying party for Nonce generation. -->
+    <bean id="openidvci.RelyingPartyForNonce" parent="shibboleth.Functions.Constant">
+        <constructor-arg>
+            <bean class="java.lang.String" c:_0="mockRelyingParty" />
+         </constructor-arg>
+    </bean>
 
 </beans>
diff --git a/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/nonce/nonce-flow.xml b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/nonce/nonce-flow.xml
index 82aa916..69ebc74 100644
--- a/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/nonce/nonce-flow.xml
+++ b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/nonce/nonce-flow.xml
@@ -16,7 +16,6 @@
     <transition on="proceed" to="BuildResponse"/>
   </action-state>
 
-
   <bean-import resource="nonce-beans.xml"/>
 
 </flow>
diff --git a/openid-vci-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
similarity index 80%
copy from openid-vci-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
copy to openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
index 4f9a82c..bb0eba3 100644
--- a/openid-vci-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
@@ -17,7 +17,7 @@
         p:placeholderPrefix="%{" p:placeholderSuffix="}" />
 
     
-    <bean id="AbstractVCIProfile" abstract="true" 
+    <bean id="AbstractVCIProfile" abstract="true"
         p:issuer="#{getObject('shibboleth.oidc.issuer')}"
         p:preauthorizedCodeLifetime="%{openidvci.preauthorizedCode:PT10M}"
         p:preauthorizedCodeLength="%{openidvci.preauthorizedCode.defaultLength:0}"        
@@ -33,33 +33,37 @@
         p:dpopProofSignatureValidationConfiguration="#{getObject('DPoPSignatureValidationConfiguration')}"
         p:dpopProofNonceGenerator="#{getObject('DefaultOAuth2DPoPNonceGenerator')}" />
     
-    <!-- TB REMOVED -->    
-    <bean id="OpenID.VCI" parent="AbstractVCIProfile" lazy-init="true"
-        class="org.geant.shibboleth.plugin.openidvci.config.impl.DefaultOpenIDVCIConfiguration"
-        p:preauthorizedCodeLifetime="%{openidvci.preauthorizedCode:PT10M}"
-        p:preauthorizedCodeLength="%{openidvci.preauthorizedCode.defaultLength:0}"/>
         
     <bean id="OpenID.VCI.CredentialOffer" parent="AbstractVCIProfile" lazy-init="true"
         class="org.geant.shibboleth.plugin.openidvci.config.impl.DefaultOpenIDVCICredentialOfferConfiguration" />
-        
+
     <bean id="OpenID.VCI.Token" parent="AbstractVCIProfile" lazy-init="true"
         class="org.geant.shibboleth.plugin.openidvci.config.impl.DefaultOpenIDVCITokenConfiguration" />
-        
+
     <bean id="OpenID.VCI.Credentials" parent="AbstractVCIProfile" lazy-init="true"
         class="org.geant.shibboleth.plugin.openidvci.config.impl.DefaultOpenIDVCICredentialsConfiguration" />
-        
+
     <bean id="OpenID.VCI.Nonce" parent="AbstractVCIProfile" lazy-init="true"
-        class="org.geant.shibboleth.plugin.openidvci.config.impl.DefaultOpenIDVCINonceConfiguration" />
+        class="org.geant.shibboleth.plugin.openidvci.config.impl.DefaultOpenIDVCINonceConfiguration"
+        p:dpopProofNonceGenerator="#{getObject('DefaultOpenIDVCINonceGenerator')}" />
         
     <!-- Property-based definition of login flows for OAuth endpoints. -->
     <bean id="openidvci.PotentialFlows"
         class="org.springframework.beans.factory.config.ListFactoryBean"
         p:sourceList="#{getObject('shibboleth.AuthenticationFlowDescriptorManager').getComponents().?[id matches 'authn/(' + '%{openidvci.authn.flows:OAuth2Client}'.trim() + ')']}" />
-        
-    <bean id="openidvci.PublicClientValidator"
-        class="org.geant.shibboleth.plugin.openidvci.authn.impl.WalletCredentialValidator" />
-        
-    <bean id="openidvci.AuthzCodeManipulationStrategy"
-        class="org.geant.shibboleth.plugin.openidvci.profile.logic.AuthorizationCodeManipulationStrategy" />
+    
+    
+    <!-- We use special DPoP Nonce generator for Nonce endpoint. -->
+    <!-- The generator does not tie client id to nonce to accomodate unprotected Nonce endpoint. -->    
+    <bean id="DefaultOpenIDVCINonceGenerator" parent="DefaultOAuth2DPoPNonceGenerator" lazy-init="true"
+        p:relyingPartyIdLookupStrategy-ref="openidvci.RelyingPartyForNonce">
+    </bean>
+
+    <!-- Mock relying party for Nonce generation. -->
+    <bean id="openidvci.RelyingPartyForNonce" parent="shibboleth.Functions.Constant">
+        <constructor-arg>
+            <bean class="java.lang.String" c:_0="mockRelyingParty" />
+         </constructor-arg>
+    </bean>
 
 </beans>
diff --git a/openid-vci-impl/src/test/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundTokenResponseMessageTest.java b/openid-vci-impl/src/test/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundTokenResponseMessageTest.java
index c18d615..7720558 100644
--- a/openid-vci-impl/src/test/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundTokenResponseMessageTest.java
+++ b/openid-vci-impl/src/test/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundTokenResponseMessageTest.java
@@ -22,7 +22,7 @@ import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.util.Arrays;
 
-import org.geant.shibboleth.plugin.openidvci.config.impl.DefaultOpenIDVCIConfiguration;
+import org.geant.shibboleth.plugin.openidvci.config.impl.DefaultOpenIDVCITokenConfiguration;
 import org.geant.shibboleth.plugin.openidvci.messaging.context.TokenContext;
 import org.geant.shibboleth.plugin.openidvci.messaging.impl.CredentialOfferRequest;
 import org.geant.shibboleth.plugin.openidvci.messaging.impl.CredentialOfferTxCode;
@@ -76,7 +76,7 @@ public class FormOutboundTokenResponseMessageTest {
                 .addSubcontext(new TokenContext());
 
         RelyingPartyContext rpCtx = ((RelyingPartyContext) profileRequestCtx.addSubcontext(new RelyingPartyContext(),
-                true)).setProfileConfig(new DefaultOpenIDVCIConfiguration());
+                true)).setProfileConfig(new DefaultOpenIDVCITokenConfiguration());
         rpCtx.setRelyingPartyId("clientID");
         BasicRelyingPartyConfiguration rpConf = new BasicRelyingPartyConfiguration();
         rpConf.setId("mock");

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


More information about the commits mailing list