[java-idp-plugin-oidc-op-oidfed] 02/02: Initial support for crit claim and custom filter strategies for cache containers

Codeberg noreply at shibboleth.net
Wed Jan 28 13:46:11 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/1a5f82b5f34a0c5f80a6d7162ef4d322d0105808

commit 1a5f82b5f34a0c5f80a6d7162ef4d322d0105808
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Wed Jan 28 15:45:34 2026 +0200

    Initial support for crit claim and custom filter strategies for cache containers
    
    - CritClaimsValidator verifies that crit claim meets syntax requirements and only contains recognized claims
      - Global default may be modified via 'idp.oidfed.cache.default.critClaims'
      - For entity configuration via 'idp.oidfed.cache.entityConfiguration.critClaims'
      - For subordinate statement via 'idp.oidfed.cache.subordinateStatement.critClaims'
    - List of custom cache container filter strategies may be wired via 'idp.oidfed.cache.<cacheType>.customFilterStrategies'
      - entityConfiguration, subordinateStatement, trustChain, resolveEntity, trustMarkStatus, trust;ark
      - Bean must be a list of matching BiFunction<CONTAINER_TYPE, MetadataFilterContext, CONTAINER_TYPE>
        - For instance BiFunction<RemoteEntityConfigurationContainer, MetadataFilterContext, RemoteEntityConfigurationContainer>
---
 ...faultEntityStatementMetadataFilterStrategy.java | 36 +++++++-
 ...rustChainSignatureValidationFilterStrategy.java | 36 +++++++-
 .../jwt/claims/impl/CritClaimsValidator.java       | 97 ++++++++++++++++++++++
 .../META-INF/net.shibboleth.idp/postconfig.xml     | 22 +++--
 .../flow/oidfed/AbstractFederationFlowTest.java    | 37 +++++++--
 .../AuthorizeFlowAutomaticRegistrationTest.java    | 42 +++++++++-
 .../EntityConfigurationMetadataCacheTest.java      | 73 +++++++++++++++-
 .../SubordinateStatementMetadataCacheTest.java     | 80 +++++++++++++++++-
 .../oidfed/cache/TrustChainMetadataCacheTest.java  | 40 +++++++++
 .../CustomEntityConfigurationFilterStrategy.java   | 58 +++++++++++++
 .../CustomSubordinateStatementFilterStrategy.java  | 58 +++++++++++++
 .../support/CustomTrustChainFilterStrategy.java    | 47 +++++++++++
 .../net/shibboleth/idp/module/conf/global.xml      | 12 +++
 .../idp/module/conf/oidfed/oidfed.properties       |  8 +-
 14 files changed, 628 insertions(+), 18 deletions(-)

diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/DefaultEntityStatementMetadataFilterStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/DefaultEntityStatementMetadataFilterStrategy.java
index b111c66..5b8a512 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/DefaultEntityStatementMetadataFilterStrategy.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/DefaultEntityStatementMetadataFilterStrategy.java
@@ -25,6 +25,7 @@ import org.slf4j.Logger;
 
 import net.shibboleth.oidc.metadata.filter.MetadataFilterContext;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
@@ -44,6 +45,17 @@ public class DefaultEntityStatementMetadataFilterStrategy
     @NonnullAfterInit
     private List<BiPredicate<BaseExpirableStatementContainer<?>, MetadataFilterContext>> validationConditions;
 
+    /** List of custom metadata filter strategies. */
+    @Nonnull private List<BiFunction<BaseExpirableStatementContainer<?>,
+        MetadataFilterContext, BaseExpirableStatementContainer<?>>> customFilterStrategies;
+
+    /**
+     * Constructor.
+     */
+    public DefaultEntityStatementMetadataFilterStrategy() {
+        customFilterStrategies = CollectionSupport.emptyList();
+    }
+
     /**
      * Set the list of validation conditions.
      * 
@@ -55,6 +67,17 @@ public class DefaultEntityStatementMetadataFilterStrategy
         validationConditions = Constraint.isNotNull(predicates, "List of validation conditions cannot be null");
     }
 
+    /**
+     * Set the list of custom metadata filter strategies.
+     * 
+     * @param strategies metadata filter strategies
+     */
+    public void setCustomFilterStrategies(@Nullable final List<BiFunction<BaseExpirableStatementContainer<?>,
+            MetadataFilterContext, BaseExpirableStatementContainer<?>>> strategies) {
+        checkSetterPreconditions();
+        customFilterStrategies = strategies == null ? CollectionSupport.emptyList() : strategies;
+    }
+
     /** {@inheritDoc} */
     @Override
     protected void doInitialize() throws ComponentInitializationException {
@@ -80,7 +103,18 @@ public class DefaultEntityStatementMetadataFilterStrategy
             }
         }
         log.debug("Metadata container successfully validated by all predicates for {}", container.getEntityId());
-        return container;
+
+        BaseExpirableStatementContainer<?> result = container;
+        for (final BiFunction<BaseExpirableStatementContainer<?>, MetadataFilterContext,
+                BaseExpirableStatementContainer<?>> customFilterStrategy : customFilterStrategies) {
+            log.trace("Applying custom filter strategy {}", customFilterStrategy);
+            result = customFilterStrategy.apply(result, filterContext);
+            if (result == null) {
+                log.debug("Custom filter strategy {} returned null", customFilterStrategy);
+                return null;
+            }
+        }
+        return result;
     }
 
 }
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustchain/DefaultRemoteTrustChainSignatureValidationFilterStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustchain/DefaultRemoteTrustChainSignatureValidationFilterStrategy.java
index 10a571c..83c4249 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustchain/DefaultRemoteTrustChainSignatureValidationFilterStrategy.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustchain/DefaultRemoteTrustChainSignatureValidationFilterStrategy.java
@@ -32,6 +32,7 @@ import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.RemoteTrus
 import net.shibboleth.oidc.metadata.filter.MetadataFilterContext;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.ThreadSafeAfterInit;
+import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
@@ -62,6 +63,17 @@ public class DefaultRemoteTrustChainSignatureValidationFilterStrategy
     @NonnullAfterInit private BiFunction<EntityStatement<?>, MetadataFilterContext, EntityStatement<?>>
         entityStatementSignatureValidationFilterStrategy;
 
+    /** List of custom metadata filter strategies. */
+    @Nonnull private List<BiFunction<RemoteTrustChainsContainer,
+        MetadataFilterContext, RemoteTrustChainsContainer>> customFilterStrategies;
+
+    /**
+     * Constructor.
+     */
+    public DefaultRemoteTrustChainSignatureValidationFilterStrategy() {
+        customFilterStrategies = CollectionSupport.emptyList();
+    }
+
     /**
      * Set the signature validation filter strategy for trust anchor entity statements.
      * 
@@ -74,6 +86,17 @@ public class DefaultRemoteTrustChainSignatureValidationFilterStrategy
                 "EntityStatementSignatureValidationFilterStrategy cannot be null");
     }
 
+    /**
+     * Set the list of custom metadata filter strategies.
+     * 
+     * @param strategies metadata filter strategies
+     */
+    public void setCustomFilterStrategies(@Nullable final List<BiFunction<RemoteTrustChainsContainer,
+            MetadataFilterContext, RemoteTrustChainsContainer>> strategies) {
+        checkSetterPreconditions();
+        customFilterStrategies = strategies == null ? CollectionSupport.emptyList() : strategies;
+    }
+
     /** {@inheritDoc} */
     @Override
     protected void doInitialize() throws ComponentInitializationException {
@@ -137,7 +160,18 @@ public class DefaultRemoteTrustChainSignatureValidationFilterStrategy
                 container.removeTrustChain(trustChain);
             }
         }
-        return container;
+
+        RemoteTrustChainsContainer result = container;
+        for (final BiFunction<RemoteTrustChainsContainer, MetadataFilterContext,
+                RemoteTrustChainsContainer> customFilterStrategy : customFilterStrategies) {
+            log.trace("Applying custom filter strategy {}", customFilterStrategy);
+            result = customFilterStrategy.apply(result, filterContext);
+            if (result == null) {
+                log.debug("Custom filter strategy {} returned null", customFilterStrategy);
+                return null;
+            }
+        }
+        return result;
     }
 
 }
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/jwt/claims/impl/CritClaimsValidator.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/jwt/claims/impl/CritClaimsValidator.java
new file mode 100644
index 0000000..4f8e692
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/jwt/claims/impl/CritClaimsValidator.java
@@ -0,0 +1,97 @@
+/*
+ * 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.Collection;
+import java.util.List;
+import java.util.Optional;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+
+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;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.primitive.NonnullSupplier;
+
+/**
+ * A {@link ClaimsValidator} for validating that crit is not an empty array, does not contain standard claim names and
+ * only contain values that are configured as recognized.
+ */
+ at ThreadSafeAfterInit
+public class CritClaimsValidator extends AbstractClaimsValidator {
+
+    /** The list of standard operators that cannot be included in the metadata_policy_crit array. */
+    @Nonnull public static final List<String> STANDARD_CLAIMS = CollectionSupport.listOf(
+            "iss", "sub", "iat", "exp", "jwks", "metadata", "crit", "authority_hints", "trust_anchor_hints",
+            "trust_marks", "trust_mark_issuers", "trust_mark_owners", "constraints", "metadata_policy",
+            "metadata_policy_crit", "source_endpoint", "aud", "trust_anchor");
+
+    /** The collection of recognized claims. */
+    @Nonnull private Collection<String> recognizedClaims;
+
+    public CritClaimsValidator() {
+        recognizedClaims = CollectionSupport.emptyList();
+    }
+
+    /**
+     * Set the collection of recognized claims
+     * 
+     * @param claims recognized claims
+     */
+    public void setRecognizedClaims(@Nullable final Collection<String> claims) {
+        checkSetterPreconditions();
+        final Collection<String> list =
+                Optional.ofNullable(claims).orElseGet(NonnullSupplier.of(CollectionSupport.emptyList()));
+        assert list != null;
+        recognizedClaims = list;
+    }
+
+    /** {@inheritDoc} */
+    protected void doValidate(@Nonnull final JWTClaimsSet claims, 
+            @Nonnull final ProfileRequestContext context) throws JWTValidationException {
+        final List<String> values;
+        try {
+            values = claims.getStringListClaim("crit");
+        } catch (final ParseException e) {
+            throw new JWTValidationException("Could not parse crit into a list of strings");
+        }
+        if (values != null) {
+            if (values.isEmpty()) {
+                throw new JWTValidationException("Empty array is not allowed for crit");
+            }
+            for (final String value : values) {
+                if (values.indexOf(value) != values.lastIndexOf(value)) {
+                    throw new JWTValidationException(
+                            "Claim " + value + " is included more than once in crit");
+                }
+                if (STANDARD_CLAIMS.contains(value)) {
+                    throw new JWTValidationException(
+                            "Claim " + value + " is standard claim name and is not allowed in crit");
+                }
+                if (!recognizedClaims.contains(value)) {
+                    throw new JWTValidationException( "Claim " + value + " is not recognized for crit");
+                }
+            }
+        }
+    }
+}
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 1f74889..12762ff 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
@@ -124,7 +124,8 @@
             </bean>
         </property>
         <property name="metadataFilterStrategy">
-            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy">
+            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy"
+                p:customFilterStrategies="#{getObject('%{idp.oidfed.cache.entityConfiguration.customFilterStrategies:}'.trim())}">
                 <property name="validationConditions">
                     <util:list value-type="java.util.function.BiPredicate">
                         <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementSignatureValidationCondition"
@@ -168,6 +169,8 @@
                     p:prohibitedClaims="source_endpoint" />
                 <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ProhibitedClaimsValidator"
                     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:}}" />
             </util:list>
         </property>
     </bean>
@@ -212,7 +215,8 @@
             </bean>
         </property>
         <property name="metadataFilterStrategy">
-            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy">
+            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy"
+                p:customFilterStrategies="#{getObject('%{idp.oidfed.cache.subordinateStatement.customFilterStrategies:}'.trim())}">
                 <property name="validationConditions">
                     <util:list value-type="java.util.function.BiPredicate">
                         <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementSignatureValidationCondition"
@@ -276,6 +280,8 @@
                     p:requiredClaims="jwks" />
                 <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator"
                     p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
+                <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.security.jwt.claims.impl.CritClaimsValidator"
+                    p:recognizedClaims="%{idp.oidfed.cache.subordinateStatement.critClaims:%{idp.oidfed.cache.default.critClaims:}}" />
                 <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.security.jwt.claims.impl.MetadataPolicyCritClaimsValidator">
                     <property name="supportedOperatorsLookupStrategy">
                         <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.security.jwt.claims.impl.DefaultMetadataPolicyOperatorsLookupStrategy"
@@ -315,7 +321,8 @@
             <bean parent="shibboleth.Functions.Expression" c:expression="#input?.getEntityId()"/>
         </property>
         <property name="metadataFilterStrategy">
-            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.trustchain.DefaultRemoteTrustChainSignatureValidationFilterStrategy">
+            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.trustchain.DefaultRemoteTrustChainSignatureValidationFilterStrategy"
+                p:customFilterStrategies="#{getObject('%{idp.oidfed.cache.trustChain.customFilterStrategies:}'.trim())}">
                 <property name="trustEngine">
                     <bean class="net.shibboleth.oidc.security.impl.ExplicitKeySignedJWTTrustEngine">
                         <constructor-arg index="0">
@@ -379,7 +386,8 @@
             <bean parent="shibboleth.Functions.Expression" c:expression="#input?.getIdentifier()"/>
         </property>
         <property name="metadataFilterStrategy">
-            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy">
+            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy"
+                p:customFilterStrategies="#{getObject('%{idp.oidfed.cache.resolveEntity.customFilterStrategies:}'.trim())}">
                 <property name="validationConditions">
                     <util:list value-type="java.util.function.BiPredicate">
                         <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementSignatureValidationCondition"
@@ -506,7 +514,8 @@
             <bean parent="shibboleth.Functions.Expression" c:expression="#input?.get(T(net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.trustmark.TrustMarkStatusIdentifierCriterion))?.getIdentifier()"/>
         </property>
         <property name="metadataFilterStrategy">
-            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy">
+            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy"
+                p:customFilterStrategies="#{getObject('%{idp.oidfed.cache.trustMarkStatus.customFilterStrategies:}'.trim())}">
                 <property name="validationConditions">
                     <util:list value-type="java.util.function.BiPredicate">
                         <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementSignatureValidationCondition"
@@ -592,7 +601,8 @@
             <bean parent="shibboleth.Functions.Expression" c:expression="#input?.get(T(net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.trustmark.TrustMarkIdentifierCriterion))?.getIdentifier()"/>
         </property>
         <property name="metadataFilterStrategy">
-            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy">
+            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy"
+                p:customFilterStrategies="#{getObject('%{idp.oidfed.cache.trustMark.customFilterStrategies:}'.trim())}">
                 <property name="validationConditions">
                     <util:list value-type="java.util.function.BiPredicate">
                         <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementSignatureValidationCondition"
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java
index fb41140..bfd6a2b 100644
--- a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java
@@ -252,6 +252,12 @@ public class AbstractFederationFlowTest extends AbstractOidcFlowTest {
     protected String rpEntityConfiguration(final String clientId, final OIDCClientMetadata metadata,
             final List<Map<String, String>> trustMarks, final JWK leafKey, final String... authorityHints)
                     throws URISyntaxException {
+        return rpEntityConfiguration(clientId, metadata, trustMarks, null, leafKey, authorityHints);
+    }
+
+    protected String rpEntityConfiguration(final String clientId, final OIDCClientMetadata metadata,
+            final List<Map<String, String>> trustMarks, final List<String> crit, final JWK leafKey,
+            final String... authorityHints) throws URISyntaxException {
         final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(clientId).subject(clientId)
                 .issueTime(Date.from(Instant.now()))
                 .expirationTime(Date.from(Instant.now().plusSeconds(300)))
@@ -262,6 +268,9 @@ public class AbstractFederationFlowTest extends AbstractOidcFlowTest {
         if (trustMarks != null) {
             builder.claim("trust_marks", trustMarks);
         }
+        if (crit != null) {
+            builder.claim("crit", crit);
+        }
         final EntityStatement<?> rpConfiguration =
                 TrustChainTestUtil.entityStatement(JWSAlgorithm.RS256, leafKey, builder.build());
         return rpConfiguration.getJwt().serialize();
@@ -391,23 +400,24 @@ public class AbstractFederationFlowTest extends AbstractOidcFlowTest {
     }
 
     protected String subordinateStatement(final String issuer, final Map<String, Object> metadata,
-            final List<String> metadataPolicyCrit) {
-        return subordinateStatement(issuer, metadata, metadataPolicyCrit, leafKey);
+            final List<String> metadataPolicyCrit, final List<String> crit) {
+        return subordinateStatement(issuer, metadata, metadataPolicyCrit, crit, leafKey);
     }
 
     protected String subordinateStatement(final String issuer, final Map<String, Object> metadata,
             final JWK subjectKey) {
-        return subordinateStatement(issuer, metadata, null, subjectKey);
+        return subordinateStatement(issuer, metadata, null, null, subjectKey);
     }
 
     protected String subordinateStatement(final String issuer, final Map<String, Object> metadata,
-            final List<String> metadataPolicyCrit, final JWK subjectKey) {
+            final List<String> metadataPolicyCrit, final List<String> crit, final JWK subjectKey) {
         final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().issuer(anchorId).subject(issuer)
                 .issueTime(Date.from(Instant.now()))
                 .expirationTime(Date.from(Instant.now().plusSeconds(300)))
                 .claim("jwks", new JWKSet(subjectKey).toJSONObject(true))
                 .claim("metadata", metadata)
                 .claim("metadata_policy_crit", metadataPolicyCrit)
+                .claim("crit", crit)
                 .build();
         final EntityStatement<?> rpConfiguration =
                 TrustChainTestUtil.entityStatement(JWSAlgorithm.RS256, trustedAnchorKey, claimsSet);
@@ -561,7 +571,24 @@ public class AbstractFederationFlowTest extends AbstractOidcFlowTest {
             mapResponse(entityConfigurationUrl(anchorId), mockResponse(trustedAnchorConfiguration()));
             mapResponse(subordinateStatementUrl(anchorFetchEndpoint, clientId),
                     mockResponse(subordinateStatement(clientId, Map.of("openid_relying_party",
-                            new OIDCClientMetadata().toJSONObject()), crit)));
+                            new OIDCClientMetadata().toJSONObject()), crit, null)));
+        } catch (UnsupportedOperationException | IOException | URISyntaxException e) {
+            Assert.fail("Could not initialize mock HTTP client", e);
+        }
+    }
+
+    protected void rpConfigureMockHttpClientWithCrit(final String clientId, final List<String> configCrit,
+            final List<String> subordinateCrit) {
+        try {
+            final OIDCClientMetadata metadata = new OIDCClientMetadata();
+            metadata.setRedirectionURI(new URI(redirectUri));
+            metadata.setJWKSet(new JWKSet(rpKey.toPublicJWK()));
+            mapResponse(entityConfigurationUrl(clientId),
+                    mockResponse(rpEntityConfiguration(clientId, metadata, null, configCrit, rpKey)));
+            mapResponse(entityConfigurationUrl(anchorId), mockResponse(trustedAnchorConfiguration()));
+            mapResponse(subordinateStatementUrl(anchorFetchEndpoint, clientId),
+                    mockResponse(subordinateStatement(clientId, Map.of("openid_relying_party",
+                            new OIDCClientMetadata().toJSONObject()), null, subordinateCrit)));
         } catch (UnsupportedOperationException | IOException | URISyntaxException e) {
             Assert.fail("Could not initialize mock HTTP client", e);
         }
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AuthorizeFlowAutomaticRegistrationTest.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AuthorizeFlowAutomaticRegistrationTest.java
index f7f3ebd..456e24a 100644
--- a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AuthorizeFlowAutomaticRegistrationTest.java
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AuthorizeFlowAutomaticRegistrationTest.java
@@ -136,6 +136,42 @@ public class AuthorizeFlowAutomaticRegistrationTest extends AbstractFederationFl
         Assert.assertEquals(result.getOutcome().getId(), "ErrorView");
     }
 
+    @Test
+    public void testWithValidTrustChain_signedRequestObject_emptyCritInConfiguration()
+            throws IOException, UnsupportedOperationException, URISyntaxException {
+        final String clientId = uniqueClientId();
+        rpConfigureMockHttpClientWithCrit(clientId, CollectionSupport.emptyList(), null);
+        final FlowExecutionResult result =
+                launchAuthenticationRequest(clientId, "openid profile", signedRequestObject(Map.of(
+                        "iss", clientId,
+                        "client_id", clientId,
+                        "aud", issuer,
+                        "exp", Instant.now().plus(Duration.ofMinutes(5)).toEpochMilli(),
+                        "jti", UUID.randomUUID(),
+                        "response_type", "code",
+                        "scope", "openid profile",
+                        "redirect_uri", redirectUri)));
+        Assert.assertEquals(result.getOutcome().getId(), "ErrorView");
+    }
+
+    @Test
+    public void testWithValidTrustChain_signedRequestObject_emptyCritInSubordinate()
+            throws IOException, UnsupportedOperationException, URISyntaxException {
+        final String clientId = uniqueClientId();
+        rpConfigureMockHttpClientWithCrit(clientId, null, CollectionSupport.emptyList());
+        final FlowExecutionResult result =
+                launchAuthenticationRequest(clientId, "openid profile", signedRequestObject(Map.of(
+                        "iss", clientId,
+                        "client_id", clientId,
+                        "aud", issuer,
+                        "exp", Instant.now().plus(Duration.ofMinutes(5)).toEpochMilli(),
+                        "jti", UUID.randomUUID(),
+                        "response_type", "code",
+                        "scope", "openid profile",
+                        "redirect_uri", redirectUri)));
+        Assert.assertEquals(result.getOutcome().getId(), "ErrorView");
+    }
+
     @Test
     public void testWithValidTrustChain_resolveApi_signedRequestObject()
             throws IOException, UnsupportedOperationException, URISyntaxException {
@@ -655,8 +691,10 @@ public class AuthorizeFlowAutomaticRegistrationTest extends AbstractFederationFl
         final AuthorizeCodeClaimsSet claims;
         try {
             claims = AuthorizeCodeClaimsSet.parse(code, getDataSealer());
-            Assert.assertNotNull(claims.getClaimsSet().getStringListClaim("oidfed_artc"));
-            return claims.getClaimsSet().getStringListClaim("oidfed_artc");
+            final JWTClaimsSet claimsSet = claims.getClaimsSet();
+            assert claimsSet != null;
+            Assert.assertNotNull(claimsSet.getStringListClaim("oidfed_artc"));
+            return claimsSet.getStringListClaim("oidfed_artc");
         } catch (ParseException | DataSealerException e) {
             return null;
         }
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/EntityConfigurationMetadataCacheTest.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/EntityConfigurationMetadataCacheTest.java
index 5eee569..ecb9009 100644
--- a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/EntityConfigurationMetadataCacheTest.java
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/EntityConfigurationMetadataCacheTest.java
@@ -36,11 +36,13 @@ import com.nimbusds.jose.jwk.JWKSet;
 import com.nimbusds.jwt.JWTClaimsSet;
 
 import net.shibboleth.idp.plugin.oidc.op.oidfed.TrustChainTestUtil;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityConfiguration;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.ResponseContainerExpirationCriterion;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.SubjectEntityIDCriterion;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.RemoteEntityConfigurationContainer;
 import net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.AbstractFederationFlowTest;
 import net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.EntityConfigurationFlowTest;
+import net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.support.CustomEntityConfigurationFilterStrategy;
 import net.shibboleth.oidc.metadata.cache.MetadataCache;
 import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
 import net.shibboleth.shared.resolver.CriteriaSet;
@@ -78,12 +80,81 @@ public class EntityConfigurationMetadataCacheTest extends AbstractFederationFlow
                             new ResponseContainerExpirationCriterion(Instant.now().plusSeconds(300))));
             Assert.assertNotNull(result);
             Assert.assertEquals(result.size(), 1);
-            Assert.assertNotNull(result.get(0).getStatement());
+            final EntityConfiguration statement = result.get(0).getStatement();
+            Assert.assertNotNull(statement);
+            assert statement != null;
+            Assert.assertNull(statement.getParsedPayload().getCustomClaims()
+                    .get(CustomEntityConfigurationFilterStrategy.CUSTOM_CLAIM_NAME));
         } catch (MetadataCacheException e) {
             Assert.fail("Could not resolve entity configuration", e);
         }
     }
 
+    @Test
+    public void testValidEntityConfiguration_customCriticalClaim()
+            throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+        final String entityId = uniqueClientId();
+        final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(entityId).subject(entityId)
+                .issueTime(Date.from(Instant.now()))
+                .expirationTime(Date.from(Instant.now().plusSeconds(300)))
+                .claim("jwks", new JWKSet(leafKey).toJSONObject(true))
+                .claim("crit", List.of("default_crit"))
+                .claim("metadata", Map.of("federation_entity", Collections.emptyMap()));
+        final String entityConfiguration = TrustChainTestUtil.signedJwt(
+                JWSAlgorithm.RS256, leafKey, "entity-statement+jwt", builder.build()).serialize();
+
+        mapResponse(entityConfigurationUrl(entityId), mockResponse(entityConfiguration));
+        try {
+            final List<RemoteEntityConfigurationContainer> result =
+                    entityConfigurationCache.get(new CriteriaSet(new SubjectEntityIDCriterion(entityId),
+                            new ResponseContainerExpirationCriterion(Instant.now().plusSeconds(300))));
+            Assert.assertNotNull(result);
+            Assert.assertEquals(result.size(), 1);
+            final EntityConfiguration statement = result.get(0).getStatement();
+            Assert.assertNotNull(statement);
+            assert statement != null;
+            Assert.assertEquals(statement.getParsedPayload().getCustomClaims()
+                    .get(CustomEntityConfigurationFilterStrategy.CUSTOM_CLAIM_NAME),
+                    CustomEntityConfigurationFilterStrategy.CUSTOM_CLAIM_VALUE);
+        } catch (MetadataCacheException e) {
+            Assert.fail("Could not resolve entity configuration", e);
+        }
+    }
+
+    @Test
+    public void testWithUnrecognizedCriticalClaim()
+            throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+        final String entityId = uniqueClientId();
+        final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(entityId).subject(entityId)
+                .issueTime(Date.from(Instant.now()))
+                .expirationTime(Date.from(Instant.now().plusSeconds(300)))
+                .claim("jwks", new JWKSet(leafKey).toJSONObject(true))
+                .claim("crit", List.of("subordinate_crit"))
+                .claim("metadata", Map.of("federation_entity", Collections.emptyMap()));
+        final String entityConfiguration = TrustChainTestUtil.signedJwt(
+                JWSAlgorithm.RS256, leafKey, "entity-statement+jwt", builder.build()).serialize();
+
+        mapResponse(entityConfigurationUrl(entityId), mockResponse(entityConfiguration));
+        assertNoEntityConfiguration(entityId);
+    }
+
+    @Test
+    public void testWithStandardClaimAsCriticalClaim()
+            throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+        final String entityId = uniqueClientId();
+        final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(entityId).subject(entityId)
+                .issueTime(Date.from(Instant.now()))
+                .expirationTime(Date.from(Instant.now().plusSeconds(300)))
+                .claim("jwks", new JWKSet(leafKey).toJSONObject(true))
+                .claim("crit", List.of("jwks"))
+                .claim("metadata", Map.of("federation_entity", Collections.emptyMap()));
+        final String entityConfiguration = TrustChainTestUtil.signedJwt(
+                JWSAlgorithm.RS256, leafKey, "entity-statement+jwt", builder.build()).serialize();
+
+        mapResponse(entityConfigurationUrl(entityId), mockResponse(entityConfiguration));
+        assertNoEntityConfiguration(entityId);
+    }
+
     @Test
     public void testSignatureWithNonMathchingKey()
             throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/SubordinateStatementMetadataCacheTest.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/SubordinateStatementMetadataCacheTest.java
index 2afb1d9..7a746c6 100644
--- a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/SubordinateStatementMetadataCacheTest.java
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/SubordinateStatementMetadataCacheTest.java
@@ -36,12 +36,14 @@ import com.nimbusds.jose.jwk.JWKSet;
 import com.nimbusds.jwt.JWTClaimsSet;
 
 import net.shibboleth.idp.plugin.oidc.op.oidfed.TrustChainTestUtil;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.SubordinateStatement;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.IssuerEntityIDCriterion;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.ResponseContainerExpirationCriterion;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.SubjectEntityIDCriterion;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.RemoteSubordinateStatementContainer;
 import net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.AbstractFederationFlowTest;
 import net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.EntityConfigurationFlowTest;
+import net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.support.CustomSubordinateStatementFilterStrategy;
 import net.shibboleth.oidc.metadata.cache.MetadataCache;
 import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
 import net.shibboleth.shared.resolver.CriteriaSet;
@@ -82,12 +84,88 @@ public class SubordinateStatementMetadataCacheTest extends AbstractFederationFlo
                             new IssuerEntityIDCriterion(anchorId)));
             Assert.assertNotNull(result);
             Assert.assertEquals(result.size(), 1);
-            Assert.assertNotNull(result.get(0).getStatement());
+            final SubordinateStatement statement = result.get(0).getStatement();
+            Assert.assertNotNull(statement);
+            assert statement != null;
+            Assert.assertNull(statement.getParsedPayload().getCustomClaims()
+                    .get(CustomSubordinateStatementFilterStrategy.CUSTOM_CLAIM_NAME));
         } catch (MetadataCacheException e) {
             Assert.fail("Could not resolve entity configuration", e);
         }
     }
 
+    @Test
+    public void testValidSubordinateStatement_customCriticalClaim()
+            throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+        final String entityId = uniqueClientId();
+        final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(anchorId).subject(entityId)
+                .issueTime(Date.from(Instant.now()))
+                .expirationTime(Date.from(Instant.now().plusSeconds(300)))
+                .claim("jwks", new JWKSet(leafKey).toJSONObject(true))
+                .claim("crit", List.of("subordinate_crit"))
+                .claim("metadata", Map.of("federation_entity", Collections.emptyMap()));
+        final String subordinateStatement = TrustChainTestUtil.signedJwt(
+                JWSAlgorithm.RS256, trustedAnchorKey, "entity-statement+jwt", builder.build()).serialize();
+
+        mapResponse(entityConfigurationUrl(anchorId), mockResponse(trustedAnchorConfiguration()));
+        mapResponse(subordinateStatementUrl(anchorFetchEndpoint, entityId),
+                mockResponse(subordinateStatement));
+        try {
+            final List<RemoteSubordinateStatementContainer> result =
+                    subordinateStatementCache.get(new CriteriaSet(new SubjectEntityIDCriterion(entityId),
+                            new ResponseContainerExpirationCriterion(Instant.now().plusSeconds(300)), 
+                            new IssuerEntityIDCriterion(anchorId)));
+            Assert.assertNotNull(result);
+            Assert.assertEquals(result.size(), 1);
+            final SubordinateStatement statement = result.get(0).getStatement();
+            Assert.assertNotNull(statement);
+            assert statement != null;
+            Assert.assertEquals(statement.getParsedPayload().getCustomClaims()
+                    .get(CustomSubordinateStatementFilterStrategy.CUSTOM_CLAIM_NAME),
+                    CustomSubordinateStatementFilterStrategy.CUSTOM_CLAIM_VALUE);
+        } catch (MetadataCacheException e) {
+            Assert.fail("Could not resolve entity configuration", e);
+        }
+    }
+
+    @Test
+    public void testWithUnrecognizedCriticalClaim()
+            throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+        final String entityId = uniqueClientId();
+        final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(anchorId).subject(entityId)
+                .issueTime(Date.from(Instant.now()))
+                .expirationTime(Date.from(Instant.now().plusSeconds(300)))
+                .claim("jwks", new JWKSet(leafKey).toJSONObject(true))
+                .claim("crit", List.of("default_crit"))
+                .claim("metadata", Map.of("federation_entity", Collections.emptyMap()));
+        final String subordinateStatement = TrustChainTestUtil.signedJwt(
+                JWSAlgorithm.RS256, trustedAnchorKey, "entity-statement+jwt", builder.build()).serialize();
+
+        mapResponse(entityConfigurationUrl(anchorId), mockResponse(trustedAnchorConfiguration()));
+        mapResponse(subordinateStatementUrl(anchorFetchEndpoint, entityId),
+                mockResponse(subordinateStatement));
+        assertNoSubordinateStatement(entityId);
+    }
+
+    @Test
+    public void testWithStandardClaimAsCriticalClaim()
+            throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+        final String entityId = uniqueClientId();
+        final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(anchorId).subject(entityId)
+                .issueTime(Date.from(Instant.now()))
+                .expirationTime(Date.from(Instant.now().plusSeconds(300)))
+                .claim("jwks", new JWKSet(leafKey).toJSONObject(true))
+                .claim("crit", List.of("jwks"))
+                .claim("metadata", Map.of("federation_entity", Collections.emptyMap()));
+        final String subordinateStatement = TrustChainTestUtil.signedJwt(
+                JWSAlgorithm.RS256, trustedAnchorKey, "entity-statement+jwt", builder.build()).serialize();
+
+        mapResponse(entityConfigurationUrl(anchorId), mockResponse(trustedAnchorConfiguration()));
+        mapResponse(subordinateStatementUrl(anchorFetchEndpoint, entityId),
+                mockResponse(subordinateStatement));
+        assertNoSubordinateStatement(entityId);
+    }
+
     @Test
     public void testSignatureWithNonMathchingKey()
             throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/TrustChainMetadataCacheTest.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/TrustChainMetadataCacheTest.java
index 432e0c8..f435d66 100644
--- a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/TrustChainMetadataCacheTest.java
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/TrustChainMetadataCacheTest.java
@@ -231,4 +231,44 @@ public class TrustChainMetadataCacheTest extends AbstractFederationFlowTest {
         }
     }
 
+    @Test
+    public void testValidTrustChainResolved_customFilterReturnsNull()
+            throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+        final String entityId = uniqueClientId();
+        
+        final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(entityId).subject(entityId)
+                .issueTime(Date.from(Instant.now()))
+                .expirationTime(Date.from(Instant.now().plusSeconds(300)))
+                .claim("jwks", new JWKSet(leafKey).toJSONObject(true))
+                .claim("metadata", Map.of("federation_entity", Collections.emptyMap()))
+                .claim("crit", List.of("default_crit"))
+                .claim("authority_hints", List.of(anchorId));
+        final String entityConfiguration = TrustChainTestUtil.signedJwt(
+                JWSAlgorithm.RS256, leafKey, "entity-statement+jwt", builder.build()).serialize();
+
+        mapResponse(entityConfigurationUrl(entityId), mockResponse(entityConfiguration));
+
+        final JWTClaimsSet.Builder builder2 = new JWTClaimsSet.Builder().issuer(anchorId).subject(entityId)
+                .issueTime(Date.from(Instant.now()))
+                .expirationTime(Date.from(Instant.now().plusSeconds(300)))
+                .claim("jwks", new JWKSet(leafKey).toJSONObject(true))
+                .claim("metadata", Map.of("federation_entity", Collections.emptyMap()));
+        final String subordinateStatement = TrustChainTestUtil.signedJwt(
+                JWSAlgorithm.RS256, trustedAnchorKey, "entity-statement+jwt", builder2.build()).serialize();
+
+        mapResponse(entityConfigurationUrl(anchorId), mockResponse(trustedAnchorConfiguration()));
+        mapResponse(subordinateStatementUrl(anchorFetchEndpoint, entityId),
+                mockResponse(subordinateStatement));
+        try {
+            final List<RemoteTrustChainsContainer> result =
+                    trustChainCache.get(new CriteriaSet(new SubjectEntityIDCriterion(entityId),
+                            new ResponseContainerExpirationCriterion(Instant.now().plusSeconds(300)), 
+                            new IssuerEntityIDCriterion(anchorId)));
+            Assert.assertNotNull(result);
+            Assert.assertEquals(result.size(), 0);
+        } catch (MetadataCacheException e) {
+            Assert.fail("Could not resolve entity configuration", e);
+        }
+    }
+
 }
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomEntityConfigurationFilterStrategy.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomEntityConfigurationFilterStrategy.java
new file mode 100644
index 0000000..15c1768
--- /dev/null
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomEntityConfigurationFilterStrategy.java
@@ -0,0 +1,58 @@
+/*
+ * 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.profile.flow.oidfed.support;
+
+import java.time.Instant;
+import java.util.Optional;
+import java.util.function.BiFunction;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityConfiguration;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.RemoteEntityConfigurationContainer;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.impl.EntityConfigurationImpl;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.impl.EntityConfigurationPayloadImpl;
+import net.shibboleth.oidc.metadata.filter.MetadataFilterContext;
+
+/**
+ * Custom filter strategy for entity configuration container.
+ */
+public class CustomEntityConfigurationFilterStrategy implements 
+    BiFunction<RemoteEntityConfigurationContainer, MetadataFilterContext, RemoteEntityConfigurationContainer>{
+
+    public static final String CUSTOM_CLAIM_NAME = "custom_claim";
+    public static final String CUSTOM_CLAIM_VALUE = "custom_value";
+
+    /** {@inheritDoc} */
+    @Override
+    public RemoteEntityConfigurationContainer apply(final RemoteEntityConfigurationContainer container,
+            final MetadataFilterContext context) {
+        final EntityConfiguration statement = container.getStatement();
+        if (statement == null) {
+            return null;
+        }
+        if (Optional.ofNullable(statement.getParsedPayload().getCritical())
+                .map(list -> list.contains("default_crit"))
+                .orElse(false)) {
+            final EntityConfigurationPayloadImpl payload =
+                    new EntityConfigurationPayloadImpl(statement.getParsedPayload());
+            payload.setCustomClaims(CUSTOM_CLAIM_NAME, CUSTOM_CLAIM_VALUE);
+            final Instant expiration = Instant.now().plusSeconds(300);
+            assert expiration != null;
+            return new RemoteEntityConfigurationContainer(statement.getSubject(),
+                    new EntityConfigurationImpl(statement.getJwt(), payload), expiration, expiration);
+        }
+        return container;
+    }
+
+}
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomSubordinateStatementFilterStrategy.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomSubordinateStatementFilterStrategy.java
new file mode 100644
index 0000000..1f17f57
--- /dev/null
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomSubordinateStatementFilterStrategy.java
@@ -0,0 +1,58 @@
+/*
+ * 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.profile.flow.oidfed.support;
+
+import java.time.Instant;
+import java.util.Optional;
+import java.util.function.BiFunction;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.SubordinateStatement;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.RemoteSubordinateStatementContainer;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.impl.SubordinateStatementImpl;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.impl.SubordinateStatementPayloadImpl;
+import net.shibboleth.oidc.metadata.filter.MetadataFilterContext;
+
+/**
+ * Custom filter strategy for subordinate statement container.
+ */
+public class CustomSubordinateStatementFilterStrategy implements 
+    BiFunction<RemoteSubordinateStatementContainer, MetadataFilterContext, RemoteSubordinateStatementContainer>{
+
+    public static final String CUSTOM_CLAIM_NAME = "custom_so_claim";
+    public static final String CUSTOM_CLAIM_VALUE = "custom_so_value";
+
+    /** {@inheritDoc} */
+    @Override
+    public RemoteSubordinateStatementContainer apply(final RemoteSubordinateStatementContainer container,
+            final MetadataFilterContext context) {
+        final SubordinateStatement statement = container.getStatement();
+        if (statement == null) {
+            return null;
+        }
+        if (Optional.ofNullable(statement.getParsedPayload().getCritical())
+                .map(list -> list.contains("subordinate_crit"))
+                .orElse(false)) {
+            final SubordinateStatementPayloadImpl payload =
+                    new SubordinateStatementPayloadImpl(statement.getParsedPayload());
+            payload.setCustomClaims(CUSTOM_CLAIM_NAME, CUSTOM_CLAIM_VALUE);
+            final Instant expiration = Instant.now().plusSeconds(300);
+            assert expiration != null;
+            return new RemoteSubordinateStatementContainer(container.getIdentifier(),
+                    new SubordinateStatementImpl(statement.getJwt(), payload), expiration, expiration);
+        }
+        return container;
+    }
+
+}
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomTrustChainFilterStrategy.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomTrustChainFilterStrategy.java
new file mode 100644
index 0000000..0c8245d
--- /dev/null
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomTrustChainFilterStrategy.java
@@ -0,0 +1,47 @@
+/*
+ * 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.profile.flow.oidfed.support;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.BiFunction;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.RemoteTrustChainsContainer;
+import net.shibboleth.oidc.metadata.filter.MetadataFilterContext;
+import net.shibboleth.shared.collection.CollectionSupport;
+
+/**
+ * Custom filter strategy for trust chains container.
+ */
+public class CustomTrustChainFilterStrategy implements 
+    BiFunction<RemoteTrustChainsContainer, MetadataFilterContext, RemoteTrustChainsContainer>{
+
+    /** {@inheritDoc} */
+    @Override
+    public RemoteTrustChainsContainer apply(final RemoteTrustChainsContainer container,
+            final MetadataFilterContext context) {
+        final List<List<EntityStatement<?>>> trustChains =
+                Optional.ofNullable(container.getTrustChains()).orElse(CollectionSupport.emptyList());
+        for (final List<EntityStatement<?>> trustChain : trustChains) {
+            final List<String> critical = trustChain.get(0).getParsedPayload().getCritical();
+            if (critical != null && critical.contains("default_crit")) {
+                return null;
+            }
+        }
+        return container;
+    }
+
+}
diff --git a/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/global.xml b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/global.xml
index 1589b62..07602b6 100644
--- a/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/global.xml
+++ b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/global.xml
@@ -97,4 +97,16 @@
         p:customObject-ref="shibboleth.HttpServletRequestSupplier"
         c:expression="'true'.equals(#custom.get().getHeader('useCustomFallbackToLocal'))" />
 
+    <util:list id="CustomEntityConfigurationFilters">
+        <bean class="net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.support.CustomEntityConfigurationFilterStrategy"/>
+    </util:list>
+
+    <util:list id="CustomSubordinateStatementFilters">
+        <bean class="net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.support.CustomSubordinateStatementFilterStrategy"/>
+    </util:list>
+
+    <util:list id="CustomTrustChainFilters">
+        <bean class="net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.support.CustomTrustChainFilterStrategy"/>
+    </util:list>
+
 </beans>
diff --git a/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed.properties b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed.properties
index d682aae..d18dc17 100644
--- a/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed.properties
+++ b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed.properties
@@ -1,4 +1,10 @@
 idp.oidfed.entity.authorityHints = https://anchor1.example.org, https://anchor2.example.org
 
 idp.oidfed.cache.entityConfiguration.invalidContainerLifetime = PT0S
-idp.oidfed.cache.entityConfiguration.minRefreshDelay = PT0S
\ No newline at end of file
+idp.oidfed.cache.entityConfiguration.minRefreshDelay = PT0S
+
+idp.oidfed.cache.default.critClaims = default_crit
+idp.oidfed.cache.entityConfiguration.customFilterStrategies = CustomEntityConfigurationFilters
+idp.oidfed.cache.subordinateStatement.customFilterStrategies = CustomSubordinateStatementFilters
+idp.oidfed.cache.subordinateStatement.critClaims = subordinate_crit
+idp.oidfed.cache.trustChain.customFilterStrategies = CustomTrustChainFilters

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


More information about the commits mailing list