[java-idp-plugin-oidc-op-oidfed] 03/03: Verify trust_marks syntax (including trust_mark_type match) as claims validator

Codeberg noreply at shibboleth.net
Fri Jan 30 11:57:13 UTC 2026


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

codeberg pushed a commit to branch dev/CACHE-REFACTOR
in repository java-idp-plugin-oidc-op-oidfed.

View the commit online:
https://codeberg.org/Shibboleth/java-idp-plugin-oidc-op-oidfed/commit/6fc88fc65fc49b7188f25e2c16f75f9fdc29b686

commit 6fc88fc65fc49b7188f25e2c16f75f9fdc29b686
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Fri Jan 30 13:56:32 2026 +0200

    Verify trust_marks syntax (including trust_mark_type match) as claims validator
    
    - the validator is wired to entity configurations, explicit registration requests and resolve entity responses
---
 .../jwt/claims/impl/TrustMarksClaimsValidator.java | 109 +++++++++++++++++++++
 .../META-INF/net.shibboleth.idp/postconfig.xml     |   3 +
 2 files changed, 112 insertions(+)

diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/jwt/claims/impl/TrustMarksClaimsValidator.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/jwt/claims/impl/TrustMarksClaimsValidator.java
new file mode 100644
index 0000000..70e9dff
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/jwt/claims/impl/TrustMarksClaimsValidator.java
@@ -0,0 +1,109 @@
+/*
+ * 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 net.shibboleth.idp.plugin.oidc.op.oidfed.security.jwt.claims.impl;
+
+import java.text.ParseException;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+
+import net.shibboleth.oidc.jwt.claims.AbstractClaimsValidator;
+import net.shibboleth.oidc.jwt.claims.ClaimsValidator;
+import net.shibboleth.oidc.jwt.claims.JWTValidationException;
+import net.shibboleth.shared.annotation.constraint.ThreadSafeAfterInit;
+
+/**
+ * A {@link ClaimsValidator} for validating the syntax of the optional trust_marks claim. Each item ie the array must
+ * contain a match between the trust_mark_type claim and the corresponding claim inside the trust_mark JWT payload.
+ */
+ at ThreadSafeAfterInit
+public class TrustMarksClaimsValidator extends AbstractClaimsValidator {
+
+    /** {@inheritDoc} */
+    protected void doValidate(@Nonnull final JWTClaimsSet claims, 
+            @Nonnull final ProfileRequestContext context) throws JWTValidationException {
+        try {
+            final List<Object> trustMarks = claims.getListClaim("trust_marks");
+            if (trustMarks != null) {
+                for (final Object raw : trustMarks) {
+                    if (raw instanceof Map<?, ?> map) {
+                        final Map<String, String> trustMark = map.keySet().stream()
+                                .filter(String.class::isInstance)
+                                .map(String.class::cast)
+                                .filter(key -> map.get(key) instanceof String)
+                                .collect(Collectors.toMap(key -> key, key -> (String) map.get(key)));
+                        if (trustMark.isEmpty()) {
+                            throw new JWTValidationException("Unexpected contents for trust_marks array item: "
+                                    + "Could not parse a map of Strings");
+                        }
+                        validateContent(trustMark);
+                    } else {
+                        throw new JWTValidationException("Unexpected contents for trust_marks array item: "
+                                + "Could not parse a map of strings");
+                    }
+                }
+            }
+        } catch (final ParseException e) {
+            throw new JWTValidationException("Unexpected contents for trust_marks: could not parse an array", e);
+        }
+    }
+
+    /**
+     * Verifies that the given trust mark item meets the syntax requirements: the trust_mark content must be a signed
+     * JWT with a matching value for the trust_mark_type claim.
+     * 
+     * @param trustMark trust mark JSON object as a map
+     * @throws JWTValidationException if the syntax validation fails
+     */
+    protected void validateContent(@Nonnull final Map<String, String> trustMark) throws JWTValidationException {
+        final String trustMarkType = trustMark.get("trust_mark_type");
+        if (trustMarkType == null) {
+            throw new JWTValidationException("Unexpected contents for trust_marks array item: "
+                    + "trust_mark_type is null");
+        }
+        final SignedJWT trustMarkJwt = Optional.ofNullable(trustMark.get("trust_mark"))
+                .map(value -> {
+                    try {
+                        return SignedJWT.parse(value);
+                    } catch (final ParseException e) {
+                        return null;
+                    }
+                })
+                .orElse(null);
+        if (trustMarkJwt == null) {
+            throw new JWTValidationException("Unexpected contents for trust_marks array item: "
+                    + "trust_mark cannot be parsed into JWT");
+        }
+        try {
+            final String trustMarkTypeJwt = trustMarkJwt.getJWTClaimsSet().getStringClaim("trust_mark_type");
+            if (!trustMarkType.equals(trustMarkTypeJwt)) {
+                throw new JWTValidationException("Unexpected contents for trust_marks array item: "
+                        + "trust_mark_type " + trustMarkType + " does not match the JWT claim " + trustMarkTypeJwt);
+            }
+        } catch (final ParseException e) {
+            throw new JWTValidationException("Unexpected contents for trust_marks array item: "
+                    + "Could not parse trust_mark_type from JWT for trust_mark_type " +  trustMarkType, e);
+        }
+      
+    }
+}
diff --git a/idp-oidfed-op-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-oidfed-op-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index af61e8f..74722fa 100644
--- a/idp-oidfed-op-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-oidfed-op-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -171,6 +171,7 @@
                     p:prohibitedClaims="aud" />
                 <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.security.jwt.claims.impl.CritClaimsValidator"
                     p:recognizedClaims="%{idp.oidfed.cache.entityConfiguration.critClaims:%{idp.oidfed.cache.default.critClaims:}}" />
+                <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.security.jwt.claims.impl.TrustMarksClaimsValidator" />
             </util:list>
         </property>
     </bean>
@@ -453,6 +454,7 @@
                     p:requiredClaims="metadata" />
                 <bean class="net.shibboleth.oidc.security.jwt.claims.impl.RequiredClaimsValidator"
                     p:requiredClaims="trust_chain" />
+                <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.security.jwt.claims.impl.TrustMarksClaimsValidator" />
             </util:list>
         </property>
     </bean>
@@ -1101,6 +1103,7 @@
                     p:recognizedClaims="%{idp.oidfed.cache.explicitRegistration.critClaims:%{idp.oidfed.cache.default.critClaims:}}" />
                 <bean class="net.shibboleth.oidc.security.jwt.claims.impl.RequiredClaimsValidator"
                     p:requiredClaims="authority_hints" />
+                <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.security.jwt.claims.impl.TrustMarksClaimsValidator" />
             </util:list>
         </property>
     </bean>

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


More information about the commits mailing list