[java-idp-plugin-vci] branch main updated: OP has done fixes JOIDC-295 and JOIDC-296, we do not need workarounds anymore
Codeberg
noreply at shibboleth.net
Thu Nov 5 07:45:42 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-idp-plugin-vci.
View the commit online:
https://codeberg.org/Shibboleth/java-idp-plugin-vci/commit/436c8f96d9f3f1229b94d6507c0b9a7b81d8d8d4
The following commit(s) were added to refs/heads/main by this push:
new 436c8f9 OP has done fixes JOIDC-295 and JOIDC-296, we do not need workarounds anymore
436c8f9 is described below
commit 436c8f96d9f3f1229b94d6507c0b9a7b81d8d8d4
Author: Janne Lauros <janne.lauros at csc.fi>
AuthorDate: Thu Nov 5 09:45:20 2026 +0200
OP has done fixes JOIDC-295 and JOIDC-296, we do not need workarounds anymore
---
README.md | 29 +----
docs/example/step-1/conf/relying-party.xml | 3 +-
docs/example/step-2/conf/relying-party.xml | 3 +-
docs/example/step-3/conf/relying-party.xml | 6 +-
docs/example/step-4/conf/relying-party.xml | 6 +-
docs/example/step-5/conf/relying-party.xml | 3 +-
...edAuthorizationDPoPJktManipulationStrategy.java | 143 ---------------------
.../META-INF/net.shibboleth.idp/postconfig.xml | 3 -
...thorizationDPoPJktManipulationStrategyTest.java | 91 -------------
9 files changed, 14 insertions(+), 273 deletions(-)
diff --git a/README.md b/README.md
index 910169d..173d774 100644
--- a/README.md
+++ b/README.md
@@ -1139,8 +1139,7 @@ and pushed authorization request (PAR):
+ p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy" />
+ <bean parent="OpenID.VCI.Credentials" />
+ <bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'none'}}"
-+ p:requireAuthenticationRequest="false"
-+ p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
++ p:requireAuthenticationRequest="false" />
+ </list>
+ </property>
+ </bean>
@@ -1673,8 +1672,7 @@ and state it per credential.
p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy" />
<bean parent="OpenID.VCI.Credentials" />
<bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'none'}}"
- p:requireAuthenticationRequest="false"
- p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
+ p:requireAuthenticationRequest="false" />
</list>
</property>
</bean>
@@ -2223,8 +2221,7 @@ Full file: [metadata/verifiable-credentials.json](docs/example/step-3/metadata/v
p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy" />
<bean parent="OpenID.VCI.Credentials" />
<bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'none'}}"
- p:requireAuthenticationRequest="false"
- p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
+ p:requireAuthenticationRequest="false" />
</list>
</property>
</bean>
@@ -2243,8 +2240,7 @@ Full file: [metadata/verifiable-credentials.json](docs/example/step-3/metadata/v
+ p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy" />
+ <bean parent="OpenID.VCI.Credentials" />
+ <bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'none'}}"
-+ p:requireAuthenticationRequest="false"
-+ p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
++ p:requireAuthenticationRequest="false" />
+ </list>
+ </property>
+ </bean>
@@ -2787,8 +2783,7 @@ Full file: [metadata/verifiable-credentials.json](docs/example/step-4/metadata/v
p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy" />
<bean parent="OpenID.VCI.Credentials" />
<bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'none'}}"
- p:requireAuthenticationRequest="false"
- p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
+ p:requireAuthenticationRequest="false" />
</list>
</property>
</bean>
@@ -2807,8 +2802,7 @@ Full file: [metadata/verifiable-credentials.json](docs/example/step-4/metadata/v
p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy" />
<bean parent="OpenID.VCI.Credentials" />
<bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'none'}}"
- p:requireAuthenticationRequest="false"
- p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
+ p:requireAuthenticationRequest="false" />
</list>
</property>
</bean>
@@ -3519,10 +3513,6 @@ idp.oauth2.authn.flows = OpenIDVCIClient
#### The HAIP wallet as a client
-> **Note**
-> `openidvci.PushedAuthorizationDPoPJktManipulationStrategy` is a workaround for the OP plugin. Once
-> the OP plugin handles pushed requests the same way, the strategy is no longer needed.
-
The client ID is the `sub` claim in the wallet’s Wallet Attestation. In our tests the EUDI wallet
provider gave the Hopae EUDI Wallet the client ID `eudiw-abca`. Configure it in *metadata/oidc-client.json*:
@@ -3670,8 +3660,7 @@ for PKCE, PAR and `iss`. Both token and PAR endpoints take the Wallet Attestatio
<bean parent="OpenID.VCI.Credentials" />
- <bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'none'}}"
+ <bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'attest_jwt_client_auth'}}"
- p:requireAuthenticationRequest="false"
- p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
+ p:requireAuthenticationRequest="false" />
</list>
</property>
</bean>
@@ -3692,10 +3681,6 @@ access tokens. The other DPoP settings are left to their defaults, so a DPoP non
give the wallet refresh tokens, bound to the key of its Wallet Attestation, see
[Refresh tokens](#refresh-tokens).
-`openidvci.PushedAuthorizationDPoPJktManipulationStrategy` on **OAUTH2.PAR** is recommended with
-DPoP, see [RFC 9449 10.1](https://www.rfc-editor.org/rfc/rfc9449#section-10.1). It is also needed
-when wallets send `resource` with a pushed request.
-
`token_endpoint_auth_methods_supported` of *static/oauth-authorization-server.json* lists the
methods your `tokenEndpointAuthMethods` allow on the token and PAR endpoints, here
`attest_jwt_client_auth` for the Hopae EUDI Wallet. `grant_types_supported` no longer lists the
diff --git a/docs/example/step-1/conf/relying-party.xml b/docs/example/step-1/conf/relying-party.xml
index 7b46c1d..5e4acfb 100644
--- a/docs/example/step-1/conf/relying-party.xml
+++ b/docs/example/step-1/conf/relying-party.xml
@@ -79,8 +79,7 @@
p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy" />
<bean parent="OpenID.VCI.Credentials" />
<bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'none'}}"
- p:requireAuthenticationRequest="false"
- p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
+ p:requireAuthenticationRequest="false" />
</list>
</property>
</bean>
diff --git a/docs/example/step-2/conf/relying-party.xml b/docs/example/step-2/conf/relying-party.xml
index 7c050b9..da27b0b 100644
--- a/docs/example/step-2/conf/relying-party.xml
+++ b/docs/example/step-2/conf/relying-party.xml
@@ -81,8 +81,7 @@
p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy" />
<bean parent="OpenID.VCI.Credentials" />
<bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'none'}}"
- p:requireAuthenticationRequest="false"
- p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
+ p:requireAuthenticationRequest="false" />
</list>
</property>
</bean>
diff --git a/docs/example/step-3/conf/relying-party.xml b/docs/example/step-3/conf/relying-party.xml
index 1b6a943..51715c9 100644
--- a/docs/example/step-3/conf/relying-party.xml
+++ b/docs/example/step-3/conf/relying-party.xml
@@ -83,8 +83,7 @@
p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy" />
<bean parent="OpenID.VCI.Credentials" />
<bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'none'}}"
- p:requireAuthenticationRequest="false"
- p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
+ p:requireAuthenticationRequest="false" />
</list>
</property>
</bean>
@@ -103,8 +102,7 @@
p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy" />
<bean parent="OpenID.VCI.Credentials" />
<bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'none'}}"
- p:requireAuthenticationRequest="false"
- p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
+ p:requireAuthenticationRequest="false" />
</list>
</property>
</bean>
diff --git a/docs/example/step-4/conf/relying-party.xml b/docs/example/step-4/conf/relying-party.xml
index edd1915..1f892f5 100644
--- a/docs/example/step-4/conf/relying-party.xml
+++ b/docs/example/step-4/conf/relying-party.xml
@@ -85,8 +85,7 @@
p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy" />
<bean parent="OpenID.VCI.Credentials" />
<bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'none'}}"
- p:requireAuthenticationRequest="false"
- p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
+ p:requireAuthenticationRequest="false" />
</list>
</property>
</bean>
@@ -105,8 +104,7 @@
p:accessTokenClaimsSetManipulationStrategy-ref="openidvci.TokenManipulationStrategy" />
<bean parent="OpenID.VCI.Credentials" />
<bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'none'}}"
- p:requireAuthenticationRequest="false"
- p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
+ p:requireAuthenticationRequest="false" />
</list>
</property>
</bean>
diff --git a/docs/example/step-5/conf/relying-party.xml b/docs/example/step-5/conf/relying-party.xml
index d53c55d..edd5b26 100644
--- a/docs/example/step-5/conf/relying-party.xml
+++ b/docs/example/step-5/conf/relying-party.xml
@@ -70,8 +70,7 @@
p:requireDpopProof="true" />
<bean parent="OpenID.VCI.Credentials" />
<bean parent="OAUTH2.PAR" p:tokenEndpointAuthMethods="#{{'attest_jwt_client_auth'}}"
- p:requireAuthenticationRequest="false"
- p:requestUriClaimsSetManipulationStrategy-ref="openidvci.PushedAuthorizationDPoPJktManipulationStrategy" />
+ p:requireAuthenticationRequest="false" />
</list>
</property>
</bean>
diff --git a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/logic/PushedAuthorizationDPoPJktManipulationStrategy.java b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/logic/PushedAuthorizationDPoPJktManipulationStrategy.java
deleted file mode 100644
index 6fd7505..0000000
--- a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/logic/PushedAuthorizationDPoPJktManipulationStrategy.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (c) 2025, GÉANT
- *
- * Licensed under the Apache License, Version 2.0 (the “License”); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an “AS IS” BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.geant.shibboleth.plugin.openidvci.profile.logic;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-
-import net.shibboleth.idp.plugin.oidc.op.messaging.context.navigate.DefaultRequestAudienceLookupFunction;
-import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultDPoPProofThumbprintLookupFunction;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-
-/**
- * This strategy is a workaround for two OP shortcomings.
- * 1) Sets 'dpop_jkt' to the thumbprint of the validated DPoP proof key of the request, when the request has no
- * 'dpop_jkt' of its own. See <a href="https://www.rfc-editor.org/rfc/rfc9449.html#section-10.1">RFC 9449
- * section 10.1</a>.
- * 2) Removes the OP's internal prefix from the 'resource' values if resource is sent as a form parameter.
- */
-public class PushedAuthorizationDPoPJktManipulationStrategy
- implements BiFunction<ProfileRequestContext, Map<String, Object>, Map<String, Object>> {
-
- /** Claim name of the DPoP proof key thumbprint. */
- @Nonnull
- public static final String DPOP_JKT = "dpop_jkt";
-
- /** Claim name of the resource indicators. */
- @Nonnull
- public static final String RESOURCE = "resource";
-
- /** Class logger. */
- @Nonnull
- private Logger log = LoggerFactory.getLogger(PushedAuthorizationDPoPJktManipulationStrategy.class);
-
- /** Strategy used to read the thumbprint of the validated DPoP proof. */
- @Nonnull
- private Function<ProfileRequestContext, String> dpopProofThumbprintLookupStrategy =
- new DefaultDPoPProofThumbprintLookupFunction();
-
- /**
- * Set the strategy used to read the thumbprint of the validated DPoP proof.
- *
- * @param strategy lookup strategy
- */
- public void setDpopProofThumbprintLookupStrategy(
- @Nonnull final Function<ProfileRequestContext, String> strategy) {
- dpopProofThumbprintLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
- }
-
- /** {@inheritDoc} */
- @Override
- @Nullable
- public Map<String, Object> apply(@Nullable final ProfileRequestContext profileRequestContext,
- @Nullable final Map<String, Object> claims) {
- if (claims == null) {
- return null;
- }
- final Map<String, Object> result = new HashMap<>(claims);
- boolean changed = removeResourcePrefix(result);
- if (result.get(DPOP_JKT) == null) {
- final String thumbprint = dpopProofThumbprintLookupStrategy.apply(profileRequestContext);
- if (thumbprint != null) {
- result.put(DPOP_JKT, thumbprint);
- log.debug("Pushed authorization request bound to DPoP proof key {}", thumbprint);
- changed = true;
- }
- }
- return changed ? result : null;
- }
-
- /**
- * Remove the prefix the OP decoders add to 'resource' values.
- *
- * 'resource' sent to PAR as a form parameter has the prefix but it should not.
- *
- * From this point on the OP logic assumes there is no prefix in the value. The authorization endpoint reads
- * the stored request as a request object, and {@link DefaultRequestAudienceLookupFunction} returns the 'resource'
- * claim of a request object as it is.
- *
- * @param claims claims set to modify
- * @return whether a value was changed
- */
- private boolean removeResourcePrefix(@Nonnull final Map<String, Object> claims) {
- final Object resource = claims.get(RESOURCE);
- if (resource instanceof String value && isPrefixed(value)) {
- claims.put(RESOURCE, removePrefix(value));
- return true;
- }
- if (resource instanceof List<?> values && values.stream().anyMatch(this::isPrefixed)) {
- claims.put(RESOURCE, values.stream()
- .map(value -> isPrefixed(value) ? removePrefix((String) value) : value)
- .toList());
- return true;
- }
- return false;
- }
-
- /**
- * Whether the value is a string with the prefix of the OP decoders.
- *
- * @param value the value
- * @return whether the value is prefixed
- */
- private boolean isPrefixed(@Nullable final Object value) {
- return value instanceof String string
- && string.startsWith(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX);
- }
-
- /**
- * Remove the prefix of the OP decoders.
- *
- * @param value prefixed value
- * @return value without the prefix
- */
- @Nonnull
- private String removePrefix(@Nonnull final String value) {
- return value.substring(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX.length());
- }
-
-}
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 a17535d..ee748a4 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
@@ -86,9 +86,6 @@
<bean id="openidvci.ClientInstanceKeyThumbprintLookup"
class="org.geant.shibboleth.plugin.openidvci.profile.context.navigate.ClientInstanceKeyThumbprintLookupFunction"
c:supplier-ref="shibboleth.HttpServletRequestSupplier" />
-
- <bean id="openidvci.PushedAuthorizationDPoPJktManipulationStrategy"
- class="org.geant.shibboleth.plugin.openidvci.profile.logic.PushedAuthorizationDPoPJktManipulationStrategy" />
<!--
diff --git a/openid-vci-impl/src/test/java/org/geant/shibboleth/plugin/openidvci/profile/logic/PushedAuthorizationDPoPJktManipulationStrategyTest.java b/openid-vci-impl/src/test/java/org/geant/shibboleth/plugin/openidvci/profile/logic/PushedAuthorizationDPoPJktManipulationStrategyTest.java
deleted file mode 100644
index 2555edd..0000000
--- a/openid-vci-impl/src/test/java/org/geant/shibboleth/plugin/openidvci/profile/logic/PushedAuthorizationDPoPJktManipulationStrategyTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (c) 2025, GÉANT
- *
- * Licensed under the Apache License, Version 2.0 (the “License”); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an “AS IS” BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.geant.shibboleth.plugin.openidvci.profile.logic;
-
-import java.util.List;
-import java.util.Map;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-/**
- * Tests for {@link PushedAuthorizationDPoPJktManipulationStrategy}.
- */
-public class PushedAuthorizationDPoPJktManipulationStrategyTest {
-
- private static final String PREFIX = "urn:shibboleth.oidc.op.resource:";
-
- private static final String ISSUER = "https://issuer.example.org";
-
- private PushedAuthorizationDPoPJktManipulationStrategy strategy(final String thumbprint) {
- final PushedAuthorizationDPoPJktManipulationStrategy strategy =
- new PushedAuthorizationDPoPJktManipulationStrategy();
- strategy.setDpopProofThumbprintLookupStrategy(prc -> thumbprint);
- return strategy;
- }
-
- @Test
- public void setsThumbprint() {
- final Map<String, Object> result = strategy("jkt").apply(new ProfileRequestContext(),
- Map.of("scope", "Diploma"));
- Assert.assertNotNull(result);
- Assert.assertEquals(result.get(PushedAuthorizationDPoPJktManipulationStrategy.DPOP_JKT), "jkt");
- }
-
- @Test
- public void keepsOwnThumbprint() {
- Assert.assertNull(strategy("jkt").apply(new ProfileRequestContext(),
- Map.of(PushedAuthorizationDPoPJktManipulationStrategy.DPOP_JKT, "own")));
- }
-
- @Test
- public void noChange() {
- Assert.assertNull(strategy(null).apply(new ProfileRequestContext(), Map.of("resource", ISSUER)));
- }
-
- @Test
- public void removesPrefixOfResource() {
- final Map<String, Object> result = strategy(null).apply(new ProfileRequestContext(),
- Map.of("resource", PREFIX + ISSUER));
- Assert.assertNotNull(result);
- Assert.assertEquals(result.get("resource"), ISSUER);
- }
-
- @Test
- public void removesPrefixOfResources() {
- final Map<String, Object> result = strategy(null).apply(new ProfileRequestContext(),
- Map.of("resource", List.of(PREFIX + ISSUER, "https://other.example.org")));
- Assert.assertNotNull(result);
- Assert.assertEquals(result.get("resource"), List.of(ISSUER, "https://other.example.org"));
- }
-
- @Test
- public void removesPrefixAndSetsThumbprint() {
- final Map<String, Object> result = strategy("jkt").apply(new ProfileRequestContext(),
- Map.of("resource", PREFIX + ISSUER));
- Assert.assertNotNull(result);
- Assert.assertEquals(result.get("resource"), ISSUER);
- Assert.assertEquals(result.get(PushedAuthorizationDPoPJktManipulationStrategy.DPOP_JKT), "jkt");
- }
-
- @Test
- public void nullClaims() {
- Assert.assertNull(strategy("jkt").apply(new ProfileRequestContext(), null));
- }
-
-}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list