[java-idp-plugin-oidc-op-oidfed] branch dev/CACHE-REFACTOR updated: Some minor refactoring, null checkup cleaning, style improvements

Codeberg noreply at shibboleth.net
Mon Dec 22 10:51:36 UTC 2025


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/5c056d26c6b4add3d3ed5a80218adf75be987967

The following commit(s) were added to refs/heads/dev/CACHE-REFACTOR by this push:
     new 5c056d2  Some minor refactoring, null checkup cleaning, style improvements
5c056d2 is described below

commit 5c056d26c6b4add3d3ed5a80218adf75be987967
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Mon Dec 22 12:51:13 2025 +0200

    Some minor refactoring, null checkup cleaning, style improvements
    
    - List<Pair<List<EntityStatement<?>>,Metadata> into List<VerifiedTrustChain>
    - Removed obsolete Jackson Object Mapper wirings
---
 .../context/RelyingPartyTrustChainContext.java     | 14 ++--
 .../oidfed/profile/context/VerifiedTrustChain.java | 76 ++++++++++++++++++++++
 .../navigate/TrustAnchorIdLookupFunction.java      |  3 +-
 .../oidfed/support/ClaimsSetExtensionSupport.java  |  9 +--
 .../support/ClientInformationExtensionSupport.java |  6 --
 .../policyrule/impl/EntityTrustMarkPolicyRule.java |  2 +-
 .../ExplicitClientRegistrationRequestDecoder.java  |  1 -
 ...efaultProvidedTrustChainValidationStrategy.java | 26 +++-----
 .../remote/IssuerEntityStatementCriterion.java     |  6 +-
 ...tityConfigurationCriteriaSetLookupFunction.java |  3 +-
 .../RemoteResolveEntityResponseContainer.java      |  2 +-
 .../DefaultTrustChainFetchingStrategy.java         | 24 +++----
 .../remote/trustmark/RemoteTrustMarkContainer.java |  2 +-
 .../FederationPolicyConstraintHelper.java          |  7 +-
 .../metadata/jackson/InstantDeserializer.java      |  5 +-
 .../metadata/jackson/JWKSetDeserializer.java       |  4 +-
 .../idp/plugin/oidc/op/oidfed/package-info.java    |  2 +-
 ...ionEntityConfigurationProfileConfiguration.java |  6 +-
 .../impl/AbstractTrustChainResolutionAction.java   |  6 +-
 .../impl/AddExtensionsToClientInformation.java     | 10 ++-
 .../profile/impl/BuildEntityConfiguration.java     | 17 +----
 .../BuildResolveEntityErrorResponseFromEvent.java  |  4 +-
 .../profile/impl/BuildResolveEntityResponse.java   | 18 ++---
 .../oidfed/profile/impl/CallResolveEntityApi.java  | 41 +++++++-----
 ...TrustMarkFromMetadataCacheFetchingFunction.java | 43 ++----------
 .../profile/impl/EntityStatementContext.java       | 13 ++--
 .../impl/InitializeEntityStatementContext.java     | 10 ++-
 .../op/oidfed/profile/impl/ResolveTrustChains.java |  6 +-
 .../op/oidfed/profile/impl/ResolveTrustMarks.java  | 16 ++---
 .../op/oidfed/profile/impl/SelectTrustChain.java   | 16 ++---
 .../profile/impl/StoreAutomaticRegistration.java   |  9 +--
 ...eAutomaticRegistrationProfileConfiguration.java | 20 +++---
 .../impl/ValidateProvidedEntityConfiguration.java  | 10 +--
 .../profile/impl/ValidateProvidedTrustChain.java   | 10 +--
 .../profile/impl/ValidateResolveEntityRequest.java |  2 +-
 .../profile/impl/ValidateSelectedTrustChain.java   | 15 ++---
 ...aultPreSelectedTrustChainIDsLookupStrategy.java |  3 -
 ...DefaultSelectedTrustChainIDsLookupStrategy.java |  2 +-
 ...dTrustChainImmediateSuperiorLookupStrategy.java |  2 +-
 ...ltSelectedTrustChainMetadataLookupStrategy.java |  2 +-
 ...electedTrustChainTrustAnchorLookupStrategy.java |  2 +-
 .../DefaultTrustAnchorHintsLookupStrategy.java     |  2 +-
 ...ultTrustChainMetadataPolicyMergingStrategy.java | 10 +--
 .../DefaultTrustChainSelectionStrategy.java        | 17 +++--
 ...onRequestEntityConfigurationLookupFunction.java |  4 +-
 ...faultEntityConfigurationCredentialResolver.java |  8 ++-
 ...DefaultPayloadJOSEObjectCredentialResolver.java |  7 +-
 ...yloadSignatureValidationCredentialResolver.java |  8 ++-
 ...aultSubordinateStatementCredentialResolver.java |  7 +-
 .../META-INF/net.shibboleth.idp/postconfig.xml     |  4 +-
 .../idp/flows/oidfed/register/register-beans.xml   |  6 +-
 51 files changed, 276 insertions(+), 272 deletions(-)

diff --git a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/RelyingPartyTrustChainContext.java b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/RelyingPartyTrustChainContext.java
index d7c03da..1ccedce 100644
--- a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/RelyingPartyTrustChainContext.java
+++ b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/RelyingPartyTrustChainContext.java
@@ -26,9 +26,7 @@ import org.opensaml.messaging.context.BaseContext;
 import com.nimbusds.jwt.SignedJWT;
 
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.TrustedRemoteResolverEntity;
-import net.shibboleth.shared.collection.Pair;
 
 /**
  * Subcontext carrying information for trust chains related to a relying party.
@@ -39,10 +37,10 @@ public final class RelyingPartyTrustChainContext extends BaseContext {
     @Nullable private List<List<EntityStatement<?>>> resolvedTrustChains;
 
     /** Policy-compliant trust chains for the relying party. */
-    @Nullable private List<Pair<List<EntityStatement<?>>,Metadata>> policyCompliantTrustChains;
+    @Nullable private List<VerifiedTrustChain> policyCompliantTrustChains;
 
     /** Selected trust chain for the relying party. */
-    @Nullable private Pair<List<EntityStatement<?>>,Metadata> selectedTrustChain;
+    @Nullable private VerifiedTrustChain selectedTrustChain;
 
     /** Expiration instant for the selected metadata. */
     @Nullable private Instant selectedMetadataExpiration;
@@ -87,7 +85,7 @@ public final class RelyingPartyTrustChainContext extends BaseContext {
      * 
      * @return the trust chains
      */
-    @Nullable public List<Pair<List<EntityStatement<?>>,Metadata>> getPolicyCompliantTrustChains() {
+    @Nullable public List<VerifiedTrustChain> getPolicyCompliantTrustChains() {
         return policyCompliantTrustChains;
     }
 
@@ -99,7 +97,7 @@ public final class RelyingPartyTrustChainContext extends BaseContext {
      * @return this context
      */
     @Nonnull public RelyingPartyTrustChainContext setPolicyCompliantTrustChains(
-            @Nullable final List<Pair<List<EntityStatement<?>>,Metadata>> chains) {
+            @Nullable final List<VerifiedTrustChain> chains) {
         policyCompliantTrustChains = chains;
         return this;
     }
@@ -109,7 +107,7 @@ public final class RelyingPartyTrustChainContext extends BaseContext {
      * 
      * @return the trust chain
      */
-    @Nullable public Pair<List<EntityStatement<?>>,Metadata> getSelectedTrustChain() {
+    @Nullable public VerifiedTrustChain getSelectedTrustChain() {
         return selectedTrustChain;
     }
 
@@ -121,7 +119,7 @@ public final class RelyingPartyTrustChainContext extends BaseContext {
      * @return this context
      */
     @Nonnull public RelyingPartyTrustChainContext setSelectedTrustChains(
-            @Nullable final Pair<List<EntityStatement<?>>,Metadata> chain) {
+            @Nullable final VerifiedTrustChain chain) {
         selectedTrustChain = chain;
         return this;
     }
diff --git a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/VerifiedTrustChain.java b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/VerifiedTrustChain.java
new file mode 100644
index 0000000..3390b46
--- /dev/null
+++ b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/VerifiedTrustChain.java
@@ -0,0 +1,76 @@
+/*
+ * 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.profile.context;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * A wrapper for a verified trust chain containing the trust chain and its policy-enforced metadata. The trust chain
+ * length is verified to equal to three or more.
+ */
+public class VerifiedTrustChain {
+
+    /** Verified trust chain. */
+    @Nonnull @NotEmpty private final List<EntityStatement<?>> trustChain;
+
+    /** Policy-enforced metadata. */
+    @Nonnull private Metadata metadata;
+
+    /**
+     * Constructor.
+     *
+     * @param chain verified trust chain
+     * @param data policy-enforced metadata
+     */
+    public VerifiedTrustChain(@Nonnull @NotEmpty final List<EntityStatement<?>> chain, @Nonnull final Metadata data) {
+        trustChain = Constraint.isNotNull(chain, "Trust chain cannot be null");
+        Constraint.isTrue(chain.size() > 2, "Trust chain cannot be shorter than three");
+        metadata = Constraint.isNotNull(data, "Metadata cannot be null");
+    }
+
+    /**
+     * Get the verified trust chain.
+     * 
+     * @return verified trust chain
+     */
+    @Nonnull @NotEmpty public List<EntityStatement<?>> getTrustChain() {
+        return trustChain;
+    }
+
+    /**
+     * Get the policy-enforced metadata.
+     * 
+     * @return policy-enforced metadata
+     */
+    @Nonnull public Metadata getMetadata() {
+        return metadata;
+    }
+
+    /**
+     * Set the policy-enforced metadata.
+     * 
+     * @param data policy-enforced metadata
+     */
+    public void setMetadata(@Nonnull Metadata data) {
+        metadata = Constraint.isNotNull(data, "Metadata cannot be null");
+    }
+}
diff --git a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/navigate/TrustAnchorIdLookupFunction.java b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/navigate/TrustAnchorIdLookupFunction.java
index 3eab558..0c7eebf 100644
--- a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/navigate/TrustAnchorIdLookupFunction.java
+++ b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/navigate/TrustAnchorIdLookupFunction.java
@@ -62,7 +62,8 @@ public class TrustAnchorIdLookupFunction implements Function<ProfileRequestConte
     @Nullable public String apply(@Nullable final ProfileRequestContext profileRequestContext) {
         return Optional.ofNullable(oidcMetadataContextLookupStrategy.apply(profileRequestContext))
                 .map(oidcContext -> oidcContext.getClientInformation())
-                .map(clientInfo -> ClientInformationExtensionSupport.parseValidatedTrustAnchor(clientInfo))
+                .map(clientInfo -> clientInfo != null ?
+                        ClientInformationExtensionSupport.parseValidatedTrustAnchor(clientInfo) : null)
                 .orElse(null);
     }
 }
diff --git a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/support/ClaimsSetExtensionSupport.java b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/support/ClaimsSetExtensionSupport.java
index 8b2a154..82707d5 100644
--- a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/support/ClaimsSetExtensionSupport.java
+++ b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/support/ClaimsSetExtensionSupport.java
@@ -58,9 +58,9 @@ public class ClaimsSetExtensionSupport {
      * @param claimsSet JWT claims set
      * @return automatically registered trust chain
      */
-    @Nullable public static List<String> parseAutoRegisteredTrustChain(@Nonnull final JWTClaimsSet claimsSet) {
+    @Nullable public static List<String> parseAutoRegisteredTrustChain(@Nullable final JWTClaimsSet claimsSet) {
         try {
-            return claimsSet.getStringListClaim(KEY_AUTO_REGISTERED_TRUST_CHAIN);
+            return claimsSet != null ? claimsSet.getStringListClaim(KEY_AUTO_REGISTERED_TRUST_CHAIN) : null;
         } catch (final ParseException e) {
             log.warn("Could not parse auto registered trust chain from the claims set", e);
         }
@@ -73,8 +73,9 @@ public class ClaimsSetExtensionSupport {
      * @param claimsSet map of claims
      * @return automatically registered trust chain
      */
-    @Nullable public static List<String> parseAutoRegisteredTrustChain(@Nonnull final Map<String,Object> claimsSet) {
-        return Optional.ofNullable(claimsSet.get(KEY_AUTO_REGISTERED_TRUST_CHAIN))
+    @Nullable public static List<String> parseAutoRegisteredTrustChain(@Nullable final Map<String,Object> claimsSet) {
+        return Optional.ofNullable(claimsSet)
+                .map(set -> set.get(KEY_AUTO_REGISTERED_TRUST_CHAIN))
                 .filter(List.class::isInstance)
                 .map(List.class::cast)
                 .map(list -> ((List<?>)list).stream()
diff --git a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/support/ClientInformationExtensionSupport.java b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/support/ClientInformationExtensionSupport.java
index c3b70c1..87539c0 100644
--- a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/support/ClientInformationExtensionSupport.java
+++ b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/support/ClientInformationExtensionSupport.java
@@ -23,12 +23,9 @@ import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import org.slf4j.Logger;
-
 import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
 
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.primitive.LoggerFactory;
 
 /**
  * Helper methods for our client information extensions related to OpenID federation.
@@ -44,9 +41,6 @@ public class ClientInformationExtensionSupport {
     /** Identifier for validated trust mark IDs within client information. */
     @Nonnull @NotEmpty public static final String KEY_VALIDATED_TRUST_MARK_IDS = "oidfed_validated_trust_mark_ids";
 
-    /** Class logger. */
-    @Nonnull private static Logger log = LoggerFactory.getLogger(ClientInformationExtensionSupport.class);
-
     /**
      * Parse validated trust anchor from the given client information.
      * 
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/attribute/filter/policyrule/impl/EntityTrustMarkPolicyRule.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/attribute/filter/policyrule/impl/EntityTrustMarkPolicyRule.java
index c58384a..0c4f36c 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/attribute/filter/policyrule/impl/EntityTrustMarkPolicyRule.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/attribute/filter/policyrule/impl/EntityTrustMarkPolicyRule.java
@@ -121,7 +121,7 @@ public class EntityTrustMarkPolicyRule extends AbstractIdentifiableInitializable
             return Tristate.FALSE;
         }
         final String entityId = Optional.ofNullable(trustChainContext.getSelectedTrustChain())
-                .map(pair -> pair.getFirst())
+                .map(verifiedChain -> verifiedChain.getTrustChain())
                 .map(chain -> chain.get(0))
                 .map(statement -> statement.getSubject())
                 .orElse(null);
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/decoding/impl/ExplicitClientRegistrationRequestDecoder.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/decoding/impl/ExplicitClientRegistrationRequestDecoder.java
index 0142a62..7669f97 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/decoding/impl/ExplicitClientRegistrationRequestDecoder.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/decoding/impl/ExplicitClientRegistrationRequestDecoder.java
@@ -40,7 +40,6 @@ import net.shibboleth.idp.plugin.oidc.op.oauth2.decoding.impl.BaseOAuth2RequestD
 import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.ExplicitClientRegistrationRequest;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityConfiguration;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.SubordinateStatement;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.impl.EntityConfigurationImpl;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.impl.SubordinateStatementImpl;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.util.EntityStatementHelper;
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/DefaultProvidedTrustChainValidationStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/DefaultProvidedTrustChainValidationStrategy.java
index 29ba25a..fc38022 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/DefaultProvidedTrustChainValidationStrategy.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/DefaultProvidedTrustChainValidationStrategy.java
@@ -25,8 +25,6 @@ import javax.annotation.Nullable;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
-
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.constraints.FederationPolicyConstraint;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.constraints.FederationPolicyConstraintHelper;
@@ -54,9 +52,6 @@ public class DefaultProvidedTrustChainValidationStrategy extends AbstractIdentif
     /** Map of supported federation policy constraints. */
     @NonnullAfterInit private Map<String, FederationPolicyConstraint> federationPolicyConstraints;
 
-    /** JSON object mapper used for decoding JSON into Map. */
-    @NonnullAfterInit private ObjectMapper objectMapper;
-
     /**
      * Set the strategy for validating trust chain signatures.
      * 
@@ -75,20 +70,20 @@ public class DefaultProvidedTrustChainValidationStrategy extends AbstractIdentif
      * 
      * @param constraints map of supported federation policy constraints.
      */
-    public void setfederationPolicyConstraints(@Nonnull final Map<String, FederationPolicyConstraint> constraints) {
+    public void setFederationPolicyConstraints(@Nonnull final Map<String, FederationPolicyConstraint> constraints) {
         checkSetterPreconditions();
         federationPolicyConstraints = Constraint.isNotNull(constraints, "Map of policy constraints cannot be null");
     }
 
     /**
-     * Set the JSON {@link ObjectMapper} used for decoding JSON into Map.
+     * Get the map of supported federation policy constraints.
      * 
-     * @param mapper object mapper
+     * @return map of supported federation policy constraints.
      */
-    public void setObjectMapper(@Nonnull final ObjectMapper mapper) {
-        checkSetterPreconditions();
-
-        objectMapper = Constraint.isNotNull(mapper, "Object mapper cannot be null");
+    @Nonnull public Map<String, FederationPolicyConstraint> getFederationPolicyConstraints() {
+        checkComponentActive();
+        assert federationPolicyConstraints != null;
+        return federationPolicyConstraints;
     }
 
     /** {@inheritDoc} */
@@ -101,9 +96,6 @@ public class DefaultProvidedTrustChainValidationStrategy extends AbstractIdentif
         if (federationPolicyConstraints == null) {
             throw new ComponentInitializationException("Map of policy constraints cannot be null");
         }
-        if (objectMapper == null) {
-            throw new ComponentInitializationException("Object mapper cannot be null");
-        }
     }
 
     /** {@inheritDoc} */
@@ -123,8 +115,10 @@ public class DefaultProvidedTrustChainValidationStrategy extends AbstractIdentif
             return false;
         }
         for (int i = 1; i < trustChain.size() - 1; i++) {
+            final List<EntityStatement<?>> subChain = trustChain.subList(0, i);
+            assert subChain != null;
             if (!FederationPolicyConstraintHelper.verifyPolicyConstraints(
-                    objectMapper, trustChain.get(i), trustChain.subList(0, i), federationPolicyConstraints)) {
+                    trustChain.get(i), subChain, getFederationPolicyConstraints())) {
                 log.debug("Subordinate statement {} policy constraints validation failed",
                         trustChain.get(i).getSubject());
                 return false;
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/IssuerEntityStatementCriterion.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/IssuerEntityStatementCriterion.java
index fb74997..51ae1ec 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/IssuerEntityStatementCriterion.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/IssuerEntityStatementCriterion.java
@@ -28,14 +28,14 @@ import net.shibboleth.shared.resolver.Criterion;
 public class IssuerEntityStatementCriterion implements Criterion {
 
     /** The entity statement value. */
-    @Nonnull final EntityStatement value;
+    @Nonnull final EntityStatement<?> value;
 
     /**
      * Constructor.
      *
      * @param statement the entity statement value, must not be null
      */
-    public IssuerEntityStatementCriterion(@Nonnull final EntityStatement statement) {
+    public IssuerEntityStatementCriterion(@Nonnull final EntityStatement<?> statement) {
         value = Constraint.isNotNull(statement, "Entity statement cannot be null");
     }
 
@@ -44,7 +44,7 @@ public class IssuerEntityStatementCriterion implements Criterion {
      * 
      * @return the entity statement value
      */
-    @Nonnull public EntityStatement getValue() {
+    @Nonnull public EntityStatement<?> getValue() {
         return value;
     }
 
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/configuration/DefaultEntityConfigurationCriteriaSetLookupFunction.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/configuration/DefaultEntityConfigurationCriteriaSetLookupFunction.java
index d778dca..fef612f 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/configuration/DefaultEntityConfigurationCriteriaSetLookupFunction.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/configuration/DefaultEntityConfigurationCriteriaSetLookupFunction.java
@@ -39,7 +39,8 @@ public class DefaultEntityConfigurationCriteriaSetLookupFunction
                 .map(container -> container.getStatement())
                 .filter(EntityConfiguration.class::isInstance)
                 .map(EntityConfiguration.class::cast)
-                .map(statement -> new CriteriaSet(new SubjectEntityStatementCriterion(statement)))
+                .map(statement -> statement != null ?
+                        new CriteriaSet(new SubjectEntityStatementCriterion(statement)) : null)
                 .orElse(null);
     }
 
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/resolver/RemoteResolveEntityResponseContainer.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/resolver/RemoteResolveEntityResponseContainer.java
index 6dcf683..07a5c25 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/resolver/RemoteResolveEntityResponseContainer.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/resolver/RemoteResolveEntityResponseContainer.java
@@ -28,7 +28,7 @@ import net.shibboleth.shared.logic.Constraint;
 /**
  * A container class for metadata caches carrying ID and statement details related to Resolve Entity.
  */
-public class RemoteResolveEntityResponseContainer extends BaseExpirableStatementContainer
+public class RemoteResolveEntityResponseContainer extends BaseExpirableStatementContainer<ResolveEntityResponse>
     implements Serializable {
 
     /** Serial version UID. */
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustchain/DefaultTrustChainFetchingStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustchain/DefaultTrustChainFetchingStrategy.java
index c019fd8..4b72d0c 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustchain/DefaultTrustChainFetchingStrategy.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustchain/DefaultTrustChainFetchingStrategy.java
@@ -28,8 +28,6 @@ import javax.annotation.Nullable;
 
 import org.slf4j.Logger;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
-
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityConfiguration;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.IssuerEntityIDCriterion;
@@ -84,9 +82,6 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
     /** Map of supported federation policy constraints. */
     @NonnullAfterInit private Map<String, FederationPolicyConstraint> federationPolicyConstraints;
 
-    /** JSON object mapper used for decoding JSON into Map. */
-    @NonnullAfterInit private ObjectMapper objectMapper;
-
     /** Strategy to fetch lifetime for container with valid contents. */
     @NonnullAfterInit private Function<CriteriaSet, Duration> criteriaToValidContainerLifetimeStrategy;
 
@@ -139,20 +134,20 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
      * 
      * @param constraints map of supported federation policy constraints.
      */
-    public void setfederationPolicyConstraints(@Nonnull final Map<String, FederationPolicyConstraint> constraints) {
+    public void setFederationPolicyConstraints(@Nonnull final Map<String, FederationPolicyConstraint> constraints) {
         checkSetterPreconditions();
         federationPolicyConstraints = Constraint.isNotNull(constraints, "Map of policy constraints cannot be null");
     }
 
     /**
-     * Set the JSON {@link ObjectMapper} used for decoding JSON into Map.
+     * Get the map of supported federation policy constraints.
      * 
-     * @param mapper object mapper
+     * @return map of supported federation policy constraints.
      */
-    public void setObjectMapper(@Nonnull final ObjectMapper mapper) {
-        checkSetterPreconditions();
-
-        objectMapper = Constraint.isNotNull(mapper, "Object mapper cannot be null");
+    @Nonnull public Map<String, FederationPolicyConstraint> getFederationPolicyConstraints() {
+        checkComponentActive();
+        assert federationPolicyConstraints != null;
+        return federationPolicyConstraints;
     }
 
     /**
@@ -199,9 +194,6 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
         if (federationPolicyConstraints == null) {
             throw new ComponentInitializationException("Map of policy constraints cannot be null");
         }
-        if (objectMapper == null) {
-            throw new ComponentInitializationException("Object mapper cannot be null");
-        }
         if (criteriaToValidContainerLifetimeStrategy == null) {
             throw new ComponentInitializationException("Criteria to valid container lifetime strategy cannot be null");
         }
@@ -395,7 +387,7 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
                         .map(id -> fetchAuthority(entityStatement, id))
                         .filter(pair -> pair != null && pair.getFirst() != null && pair.getSecond() != null)
                         .filter(pair -> FederationPolicyConstraintHelper.verifyPolicyConstraints(
-                                objectMapper, pair.getSecond(), chain, federationPolicyConstraints))
+                                pair.getSecond(), chain, getFederationPolicyConstraints()))
                         .toList();
                 hints = !authorities.isEmpty();
                 authorities.forEach(authority -> {
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustmark/RemoteTrustMarkContainer.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustmark/RemoteTrustMarkContainer.java
index eeed549..2dbe6ee 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustmark/RemoteTrustMarkContainer.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustmark/RemoteTrustMarkContainer.java
@@ -28,7 +28,7 @@ import net.shibboleth.shared.logic.Constraint;
 /**
  * A container class for metadata caches carrying ID and statement details related to Trust Mark.
  */
-public class RemoteTrustMarkContainer extends BaseExpirableStatementContainer implements Serializable {
+public class RemoteTrustMarkContainer extends BaseExpirableStatementContainer<TrustMark> implements Serializable {
 
     /** Serial version UID. */
     private static final long serialVersionUID = 756269369356865370L;
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/constraints/FederationPolicyConstraintHelper.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/constraints/FederationPolicyConstraintHelper.java
index 9a74b3f..75aece5 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/constraints/FederationPolicyConstraintHelper.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/constraints/FederationPolicyConstraintHelper.java
@@ -22,8 +22,6 @@ import javax.annotation.Nullable;
 
 import org.slf4j.Logger;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
-
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.SubordinateStatementPayload;
 import net.shibboleth.shared.primitive.LoggerFactory;
@@ -40,14 +38,13 @@ public class FederationPolicyConstraintHelper {
      * Parses and verifies federation policy constraints set in the given subordinates statement against the given
      * trust chain.
      * 
-     * @param objectMapper object mapper used for parsing the constraints claim
      * @param subordinateStatement subordinate statement containing the constraints
      * @param trustChain trust chain to be verified
      * @param federationPolicyConstraints map of the federation policy constraint implementations
      * @return true if trust chain meets the constraints. false otherwise
      */
-    public static boolean verifyPolicyConstraints(@Nonnull final ObjectMapper objectMapper,
-            @Nullable final EntityStatement<?> subordinateStatement, @Nonnull final List<EntityStatement<?>> trustChain,
+    public static boolean verifyPolicyConstraints(@Nullable final EntityStatement<?> subordinateStatement,
+            @Nonnull final List<EntityStatement<?>> trustChain,
             @Nonnull Map<String, FederationPolicyConstraint> federationPolicyConstraints) {
         if (subordinateStatement == null) {
             return true;
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/jackson/InstantDeserializer.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/jackson/InstantDeserializer.java
index b16bce8..80fdd1b 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/jackson/InstantDeserializer.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/jackson/InstantDeserializer.java
@@ -44,6 +44,9 @@ public class InstantDeserializer extends JsonDeserializer<Instant> {
             throws IOException, JsonProcessingException {
 
         final JavaType longType = TypeFactory.defaultInstance().constructType(Long.class);
-        return Instant.ofEpochSecond(deserializationContext.readValue(jsonParser, longType));
+        final Long longValue = deserializationContext.readValue(jsonParser, longType);
+        final Instant result = Instant.ofEpochSecond(longValue);
+        assert result != null;
+        return result;
     }
 }
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/jackson/JWKSetDeserializer.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/jackson/JWKSetDeserializer.java
index b458b03..f5c0762 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/jackson/JWKSetDeserializer.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/jackson/JWKSetDeserializer.java
@@ -61,7 +61,9 @@ public class JWKSetDeserializer extends JsonDeserializer<JWKSet> {
 
         final Map<String,Object> map = deserializationContext.readValue(jsonParser, objectMapType);
         try {
-            return JWKSet.parse(map);
+            final JWKSet result = JWKSet.parse(map);
+            assert result != null;
+            return result;
         } catch (final ParseException e) {
             throw new IOException("Could not parse JWKSet from the given input", e);
         }
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/package-info.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/package-info.java
index a097e1a..b42f2b4 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/package-info.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/package-info.java
@@ -13,6 +13,6 @@
  */
 
 /**
- * Top level OIDC OP plugin classes.
+ * Top level OIDC OP federation plugin classes.
  */
 package net.shibboleth.idp.plugin.oidc.op.oidfed;
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/config/impl/DefaultOIDFederationEntityConfigurationProfileConfiguration.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/config/impl/DefaultOIDFederationEntityConfigurationProfileConfiguration.java
index d3b542b..c72fc6b 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/config/impl/DefaultOIDFederationEntityConfigurationProfileConfiguration.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/config/impl/DefaultOIDFederationEntityConfigurationProfileConfiguration.java
@@ -205,10 +205,12 @@ public class DefaultOIDFederationEntityConfigurationProfileConfiguration
 
     /** {@inheritDoc} */
     @Override
-    @Nullable
+    @Nonnull
     public Map<String,Function<ProfileRequestContext,Object>> getOptionalClaimsLookupStrategies(
             @Nullable final ProfileRequestContext profileRequestContext) {
-        return optionalClaimsLookupStrategiesLookupStrategy.apply(profileRequestContext);
+        final Map<String,Function<ProfileRequestContext,Object>> strategies =
+                optionalClaimsLookupStrategiesLookupStrategy.apply(profileRequestContext);
+        return strategies != null ? strategies : CollectionSupport.emptyMap();
     }
 
     /**
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/AbstractTrustChainResolutionAction.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/AbstractTrustChainResolutionAction.java
index 849b731..2fa7d26 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/AbstractTrustChainResolutionAction.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/AbstractTrustChainResolutionAction.java
@@ -32,10 +32,10 @@ import org.slf4j.Logger;
 
 import net.minidev.json.JSONObject;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.impl.MetadataImpl;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.policy.FederationMetadataPolicyHelper;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.RelyingPartyTrustChainContext;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.VerifiedTrustChain;
 import net.shibboleth.idp.profile.AbstractProfileAction;
 import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
@@ -240,7 +240,7 @@ public class AbstractTrustChainResolutionAction extends AbstractProfileAction {
      * @return error event ID if metadata policy merging or enforcement failed, null otherwise
      */
     @Nullable protected String populatePolicyComplaintChains(@Nonnull final List<EntityStatement<?>> chain,
-            @Nonnull List<Pair<List<EntityStatement<?>>, Metadata>> policyCompliantChains) {
+            @Nonnull List<VerifiedTrustChain> policyCompliantChains) {
         final Map<String,Map<String,Object>> metadata = getMetadataLookupStrategy().apply(chain);
         log.trace("{} Metadata resolved via lookup strategy: {}", getLogPrefix(), metadata);
         if (metadata != null) {
@@ -277,7 +277,7 @@ public class AbstractTrustChainResolutionAction extends AbstractProfileAction {
                             chain.get(0).getSubject(), entityType);
                 }
             }
-            policyCompliantChains.add(new Pair<>(chain, new MetadataImpl(verifiedMetadata)));
+            policyCompliantChains.add(new VerifiedTrustChain(chain, new MetadataImpl(verifiedMetadata)));
             log.debug("{} Policy-enforced metadata {}", getLogPrefix(), verifiedMetadata);
             return null;
         }
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/AddExtensionsToClientInformation.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/AddExtensionsToClientInformation.java
index 2bb5a9f..6e8a347 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/AddExtensionsToClientInformation.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/AddExtensionsToClientInformation.java
@@ -33,12 +33,11 @@ import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
 
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCClientRegistrationResponseContext;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.RelyingPartyTrustChainContext;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.VerifiedTrustChain;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.support.ClientInformationExtensionSupport;
 import net.shibboleth.idp.profile.AbstractProfileAction;
 import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.collection.Pair;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
 
@@ -64,7 +63,7 @@ public class AddExtensionsToClientInformation extends AbstractProfileAction {
     @NonnullBeforeExec private  RelyingPartyTrustChainContext trustChainContext;
 
     /** Selected trust chain to operate on. */
-    @NonnullBeforeExec private Pair<List<EntityStatement<?>>, Metadata> selectedTrustChain;
+    @NonnullBeforeExec private VerifiedTrustChain selectedTrustChain;
 
     /** Constructor. */
     public AddExtensionsToClientInformation() {
@@ -133,8 +132,7 @@ public class AddExtensionsToClientInformation extends AbstractProfileAction {
         }
 
         selectedTrustChain = trustChainContext.getSelectedTrustChain();
-        assert selectedTrustChain != null;
-        if (selectedTrustChain.getFirst() == null || selectedTrustChain.getSecond() == null) {
+        if (selectedTrustChain == null) {
             log.error("{} Selected trust chain contents is not populated", getLogPrefix());
             ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
             return false;
@@ -147,7 +145,7 @@ public class AddExtensionsToClientInformation extends AbstractProfileAction {
     @Override
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
 
-        final List<EntityStatement<?>> trustChain = selectedTrustChain.getFirst();
+        final List<EntityStatement<?>> trustChain = selectedTrustChain.getTrustChain();
         assert trustChain != null;
 
         clientInformation.getOIDCMetadata().setCustomField(
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildEntityConfiguration.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildEntityConfiguration.java
index 0bb793d..a6fed36 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildEntityConfiguration.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildEntityConfiguration.java
@@ -33,6 +33,7 @@ import com.nimbusds.jose.jwk.JWK;
 import com.nimbusds.jose.jwk.JWKSet;
 import com.nimbusds.jwt.JWTClaimsSet;
 
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.config.navigate.AuthorityHintsLookupFunction;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.config.navigate.EntityStatementLifetimeLookupFunction;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.config.navigate.OptionalClaimsLookupStrategiesLookupFunction;
@@ -42,7 +43,6 @@ import net.shibboleth.oidc.security.CredentialConversionUtil;
 import net.shibboleth.oidc.security.jose.SignatureSigningConfiguration;
 import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
 import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
 
@@ -76,7 +76,7 @@ public class BuildEntityConfiguration extends AbstractBuildEntityStatementAction
         optionalClaimsLookupStrategiesLookupStrategy;
 
     /** Metadata to publish. */
-    @NonnullBeforeExec private Map<String,Map<String,Object>> metadata;
+    @NonnullBeforeExec private Metadata metadata;
 
     /** Constructor. */
     public BuildEntityConfiguration() {
@@ -136,17 +136,6 @@ public class BuildEntityConfiguration extends AbstractBuildEntityStatementAction
                 Constraint.isNotNull(strategy, "Optional claims lookup strategies lookup strategy cannot be null");
     }
 
-    /** {@inheritDoc} */
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-
-        if (optionalClaimsLookupStrategiesLookupStrategy == null) {
-            throw new ComponentInitializationException(
-                    "Optional claims lookup strategies lookup strategy cannot be null");
-        }
-    }
-
     /** {@inheritDoc} */
     @Override
     protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
@@ -199,7 +188,7 @@ public class BuildEntityConfiguration extends AbstractBuildEntityStatementAction
 
         builder.expirationTime(Date.from(dateExp));
         builder.claim("jwks", new JWKSet(jwks).toJSONObject(true));
-        builder.claim("metadata", CollectionSupport.copyToMap(metadata));
+        builder.claim("metadata", CollectionSupport.copyToMap(metadata.getAllClaims()));
         final List<String> authorityHints = authorityHintsLookupStrategy.apply(profileRequestContext);
         if (authorityHints != null && !authorityHints.isEmpty()) {
             builder.claim("authority_hints", authorityHints);
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildResolveEntityErrorResponseFromEvent.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildResolveEntityErrorResponseFromEvent.java
index 3b61241..93b382d 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildResolveEntityErrorResponseFromEvent.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildResolveEntityErrorResponseFromEvent.java
@@ -234,8 +234,8 @@ public class BuildResolveEntityErrorResponseFromEvent extends AbstractProfileAct
         }
     }
 
-    protected JSONErrorResponse buildErrorResponse(final ErrorObject error,
-            final ProfileRequestContext profileRequestContext) {
+    protected JSONErrorResponse buildErrorResponse(@Nonnull final ErrorObject error,
+            @Nonnull final ProfileRequestContext profileRequestContext) {
         final JSONErrorResponse response = new JSONErrorResponse(error);
         final RelyingPartyCachedMessageContext resolveEntityContext =
                 resolveEntityContextLookupStrategy.apply(profileRequestContext);
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildResolveEntityResponse.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildResolveEntityResponse.java
index 5e42d49..aa341d4 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildResolveEntityResponse.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildResolveEntityResponse.java
@@ -40,6 +40,7 @@ import com.nimbusds.jwt.SignedJWT;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.RelyingPartyTrustChainContext;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.VerifiedTrustChain;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultEntityTypesLookupFunction;
 import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
 import net.shibboleth.shared.collection.Pair;
@@ -117,19 +118,13 @@ public class BuildResolveEntityResponse extends AbstractBuildEntityStatementActi
     @Override
     protected boolean populateClaimsSetBuilder(@Nonnull final JWTClaimsSet.Builder builder,
             @Nonnull final ProfileRequestContext profileRequestContext) {
-        final Pair<List<EntityStatement<?>>,Metadata> selectedTrustChain =
-                trustChainContext.getSelectedTrustChain();
+        final VerifiedTrustChain selectedTrustChain = trustChainContext.getSelectedTrustChain();
         if (selectedTrustChain == null) {
             log.debug("{} No selected trust chain found form the context", getLogPrefix());
             ActionSupport.buildEvent(profileRequestContext, OidFederationEventIds.INVALID_METADATA);
             return false;                                    
         }
-        final Metadata metadata = selectedTrustChain.getSecond();
-        if (metadata == null) {
-            log.debug("{} No metadata set for the selected trust chain", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, OidFederationEventIds.INVALID_METADATA);
-            return false;                        
-        }
+        final Metadata metadata = selectedTrustChain.getMetadata();
         final List<String> entityTypes = entityTypesLookupStrategy.apply(profileRequestContext);
         log.trace("{} The following entity types were requested: {}", getLogPrefix(), entityTypes);
         if (entityTypes != null && !entityTypes.isEmpty()) {
@@ -148,12 +143,7 @@ public class BuildResolveEntityResponse extends AbstractBuildEntityStatementActi
             builder.claim("metadata", metadata);
         }
 
-        final List<EntityStatement<?>> trustChain = selectedTrustChain.getFirst();
-        if (trustChain == null || trustChain.isEmpty()) {
-            log.debug("{} No selected trust chain set in the trust chain context", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, OidFederationEventIds.INVALID_METADATA);
-            return false;
-        }
+        final List<EntityStatement<?>> trustChain = selectedTrustChain.getTrustChain();
         builder.claim("trust_chain", trustChain.stream()
                 .map(statement -> statement.getJwt().serialize())
                 .toList());
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/CallResolveEntityApi.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/CallResolveEntityApi.java
index 7395973..d2d9f61 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/CallResolveEntityApi.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/CallResolveEntityApi.java
@@ -61,6 +61,7 @@ import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.ResolveEntityRe
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.TrustedRemoteResolverEntity;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.RelyingPartyTrustChainContext;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.VerifiedTrustChain;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultPreSelectedTrustChainIDsLookupStrategy;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultTrustChainIDsLookupStrategy;
 import net.shibboleth.idp.profile.AbstractProfileAction;
@@ -69,9 +70,9 @@ import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
 import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.collection.Pair;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.ConstraintViolationException;
 import net.shibboleth.shared.logic.FunctionSupport;
 import net.shibboleth.shared.logic.PredicateSupport;
 import net.shibboleth.shared.primitive.LoggerFactory;
@@ -336,9 +337,10 @@ public class CallResolveEntityApi extends AbstractProfileAction {
     /** {@inheritDoc} */
     @Override
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-        log.debug("{} Resolving trust chain via resolve entity API for {}", getLogPrefix(), clientId);
-        assert clientId != null;
-        final CriteriaSet baseCriteriaSet = new CriteriaSet(new SubjectEntityIDCriterion(clientId));
+        final String nonNullClientId = clientId;
+        assert nonNullClientId != null;
+        log.debug("{} Resolving trust chain via resolve entity API for {}", getLogPrefix(), nonNullClientId);
+        final CriteriaSet baseCriteriaSet = new CriteriaSet(new SubjectEntityIDCriterion(nonNullClientId));
         final EntityConfiguration entityConfiguration = entityConfigurationLookupStrategy.apply(profileRequestContext);
         if (entityConfiguration != null) {
             log.debug("{} Entity configuration resolved and included to the criteria set", getLogPrefix());
@@ -354,7 +356,9 @@ public class CallResolveEntityApi extends AbstractProfileAction {
             ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
             return;
         }
-        baseCriteriaSet.add(new ResponseContainerExpirationCriterion(Instant.now().plus(cachedLifetime)));
+        final Instant expirationTime = Instant.now().plus(cachedLifetime);
+        assert expirationTime != null;
+        baseCriteriaSet.add(new ResponseContainerExpirationCriterion(expirationTime));
 
         final List<String> preSelectedChain =
                 Optional.ofNullable(preSelectedTrustChainIdsLookupStrategy.apply(profileRequestContext))
@@ -381,7 +385,7 @@ public class CallResolveEntityApi extends AbstractProfileAction {
                         CollectionSupport.copyToList(alreadyAttemptedEntities));
             }
             final CriteriaSet criteria = new CriteriaSet(new SubjectEntityIDCriterion(trustedEntity.getEntityId()),
-                    new ResponseContainerExpirationCriterion(Instant.now().plus(cachedLifetime)));
+                    new ResponseContainerExpirationCriterion(expirationTime));
 
             final List<RemoteEntityConfigurationContainer> configurationCacheResult;
             try {
@@ -403,9 +407,12 @@ public class CallResolveEntityApi extends AbstractProfileAction {
                 log.warn("{} Could not fetch federation resolve endpoint for {}", getLogPrefix(), trustedEntity);
                 continue;
             }
-            final RemoteResolveEntityCacheContainerIdentifier entityRequest = new RemoteResolveEntityCacheContainerIdentifier(
-                    uri.toString(), clientId, List.copyOf(trustedEntity.getTrustAnchors()),
-                    List.of("openid_relying_party"));
+            final String uriValue = uri.toString();
+            assert uriValue != null;
+            final RemoteResolveEntityCacheContainerIdentifier entityRequest =
+                    new RemoteResolveEntityCacheContainerIdentifier(
+                            uriValue, nonNullClientId, CollectionSupport.copyToList(trustedEntity.getTrustAnchors()),
+                            entityTypesLookupStrategy.apply(profileRequestContext));
             final CriteriaSet criteriaSet = new CriteriaSet();
             baseCriteriaSet.forEach(c -> criteriaSet.add(c));
             criteriaSet.add(new RemoteResolveEntityCacheIdentifierCriterion(entityRequest));
@@ -420,8 +427,9 @@ public class CallResolveEntityApi extends AbstractProfileAction {
                 log.debug("{} No data resolved for {} from {}", getLogPrefix(), clientId, trustedEntity);
                 continue;
             }
-            if (cacheResult.get(0).getStatement() instanceof ResolveEntityResponse successResponse) {
-                final ResolveEntityResponsePayload payload = successResponse.getParsedPayload();
+            final ResolveEntityResponse statement = cacheResult.get(0).getStatement();
+            if (statement != null) {
+                final ResolveEntityResponsePayload payload = statement.getParsedPayload();
                 final List<String> rawTrustChain = payload.getTrustChain();
                 final Metadata metadata = payload.getMetadata();
                 final List<Map<String,String>> rawTrustMarks = payload.getTrustMarks();
@@ -447,7 +455,7 @@ public class CallResolveEntityApi extends AbstractProfileAction {
                                     assert objectMapper != null;
                                     chain.add(SubordinateStatementImpl.parse(jwt, objectMapper));
                                 }
-                            } catch (final JsonProcessingException | ParseException e) {
+                            } catch (final JsonProcessingException | ParseException | ConstraintViolationException e) {
                                 log.warn("Could not parse the trust chain into list of entity statements", e);
                             }
                         });
@@ -456,9 +464,8 @@ public class CallResolveEntityApi extends AbstractProfileAction {
                             getLogPrefix());
                     continue;
                 }
-                final List<Pair<List<EntityStatement<?>>, Metadata>> policyCompliantChains =
-                        new ArrayList<>();
-                policyCompliantChains.add(new Pair<>(chain, metadata));
+                final List<VerifiedTrustChain> policyCompliantChains = new ArrayList<>();
+                policyCompliantChains.add(new VerifiedTrustChain(chain, metadata));
                 trustChainContext.setPolicyCompliantTrustChains(policyCompliantChains);
                 log.debug("{} Populated policy compliant trust chains with {}", getLogPrefix(), policyCompliantChains);
 
@@ -506,8 +513,8 @@ public class CallResolveEntityApi extends AbstractProfileAction {
                 }
                 return;
             } else {
-                log.debug("{} The response from {} was not a success response: {}", getLogPrefix(), trustedEntity,
-                        cacheResult.get(0).getStatement());
+                log.debug("{} The cached response from {} was not containing statement", getLogPrefix(),
+                        trustedEntity);
                 continue;
             }
         }
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/DefaultTrustMarkFromMetadataCacheFetchingFunction.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/DefaultTrustMarkFromMetadataCacheFetchingFunction.java
index b8bedfc..aa6b859 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/DefaultTrustMarkFromMetadataCacheFetchingFunction.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/DefaultTrustMarkFromMetadataCacheFetchingFunction.java
@@ -15,8 +15,6 @@
 package net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl;
 
 import java.net.URI;
-import java.time.Duration;
-import java.time.Instant;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
@@ -27,10 +25,7 @@ import javax.annotation.Nullable;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
-
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.TrustMark;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.ResponseContainerExpirationCriterion;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.RemoteEntityConfigurationContainer;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.trustmark.RemoteTrustMarkContainer;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.trustmark.TrustMarkCacheIdentifier;
@@ -63,9 +58,6 @@ public class DefaultTrustMarkFromMetadataCacheFetchingFunction extends AbstractI
     /** Cache containing responses from Trust Mark APIs. */
     @NonnullAfterInit private MetadataCache<RemoteTrustMarkContainer> trustMarkCache;
 
-    /** JSON object mapper used for decoding JSON into Map. */
-    @NonnullAfterInit private ObjectMapper objectMapper;
-
     /** Trusted trust mark API entity. */
     @NonnullAfterInit private TrustedRemoteEntity trustedEntity;
 
@@ -75,16 +67,6 @@ public class DefaultTrustMarkFromMetadataCacheFetchingFunction extends AbstractI
     /** Subject of the trust mark. */
     @NonnullAfterInit private String subject;
 
-    /** Lifetime for the cached object. */
-    @Nonnull private Duration cachedLifetime;
-
-    /**
-     * Constructor.
-     */
-    public DefaultTrustMarkFromMetadataCacheFetchingFunction() {
-        cachedLifetime = Duration.ofHours(1);
-    }
-    
     /**
      * Set the cache used to fetch the issuer entity configuration from.
      * 
@@ -105,16 +87,6 @@ public class DefaultTrustMarkFromMetadataCacheFetchingFunction extends AbstractI
         trustMarkCache = Constraint.isNotNull(cache, "Trust Mark cache cannot be null");
     }
 
-    /**
-     * Set the JSON {@link ObjectMapper} used for decoding JSON into Map.
-     * 
-     * @param mapper object mapper
-     */
-    public void setObjectMapper(@Nonnull final ObjectMapper mapper) {
-        checkSetterPreconditions();
-        objectMapper = Constraint.isNotNull(mapper, "Object mapper cannot be null");
-    }
-
     /**
      * Set the trusted trust mark API entity.
      * 
@@ -154,9 +126,6 @@ public class DefaultTrustMarkFromMetadataCacheFetchingFunction extends AbstractI
         if (trustMarkCache == null) {
             throw new ComponentInitializationException("Trust Mark cache cannot be null");
         }
-        if (objectMapper == null) {
-            throw new ComponentInitializationException("Object mapper cannot be null");
-        }
         if (trustedEntity == null) {
             throw new ComponentInitializationException("Trusted entity cannot be null");
         }
@@ -173,16 +142,18 @@ public class DefaultTrustMarkFromMetadataCacheFetchingFunction extends AbstractI
     public Map<String, String> apply(@Nullable final ProfileRequestContext profileRequestContext) {
         checkComponentActive();
         final String entityId = trustedEntity.getEntityId();
+        assert entityConfigurationCache != null;
         final URI uri = EntityStatementHelper.fetchEndpointUriFromFederationEntity(entityConfigurationCache,
                 entityId, "trust_mark_endpoint");
         if (uri == null) {
             log.warn("Could not fetch trust mark endpoint for {}", entityId);
             return null;
         }
+        final String uriValue = uri.toString();
+        assert uriValue != null; assert trustMarkType != null; assert subject != null;
         final TrustMarkCacheIdentifier trustMarkIdentifier =
-                new TrustMarkCacheIdentifier(uri.toString(), trustMarkType, subject);
+                new TrustMarkCacheIdentifier(uriValue, trustMarkType, subject);
         final CriteriaSet criteriaSet = new CriteriaSet(new TrustMarkIdentifierCriterion(trustMarkIdentifier));
-        criteriaSet.add(new ResponseContainerExpirationCriterion(Instant.now().plus(cachedLifetime)));
         final List<RemoteTrustMarkContainer> cacheResult;
         try {
             cacheResult = trustMarkCache.get(criteriaSet);
@@ -194,11 +165,11 @@ public class DefaultTrustMarkFromMetadataCacheFetchingFunction extends AbstractI
             log.debug("No data resolved for {} from {}", trustMarkType, trustedEntity);
             return null;
         }
-        if (cacheResult.get(0).getStatement() instanceof TrustMark trustMark) {
+        final TrustMark trustMark = cacheResult.get(0).getStatement();
+        if (trustMark != null) {
             return Map.of("trust_mark_type", trustMarkType, "trust_mark", trustMark.getJwt().serialize());
         } else {
-            log.debug("The response from {} was not a success response: {}", trustedEntity,
-                    cacheResult.get(0).getStatement());
+            log.debug("The cache container for {} did not contain trust mark", trustedEntity);
         }
         return null;
     }
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/EntityStatementContext.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/EntityStatementContext.java
index ce1a8e4..2b155bf 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/EntityStatementContext.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/EntityStatementContext.java
@@ -15,7 +15,6 @@
 package net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl;
 
 import java.time.Duration;
-import java.util.Map;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -24,13 +23,15 @@ import org.opensaml.messaging.context.BaseContext;
 
 import com.nimbusds.jwt.JWT;
 
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
+
 /**
  * Subcontext carrying information used to produce entity statements.
  */
 public final class EntityStatementContext extends BaseContext {
 
     /** Metadata. */
-    @Nullable private Map<String,Map<String,Object>> metadata;
+    @Nullable private Metadata metadata;
 
     /** Lifetime of the statement. */
     @Nullable private Duration lifetime;
@@ -43,19 +44,19 @@ public final class EntityStatementContext extends BaseContext {
      * 
      * @return the metadata
      */
-    @Nullable public Map<String,Map<String,Object>> getMetadata() {
+    @Nullable public Metadata getMetadata() {
         return metadata;
     }
 
     /**
      * Set the metadata.
      * 
-     * @param map the metadata
+     * @param data the metadata
      * 
      * @return this context
      */
-    @Nonnull public EntityStatementContext setMetadata(@Nullable final Map<String,Map<String,Object>> map) {
-        metadata = map;
+    @Nonnull public EntityStatementContext setMetadata(@Nullable final Metadata data) {
+        metadata = data;
         return this;
     }
     
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/InitializeEntityStatementContext.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/InitializeEntityStatementContext.java
index 59b5f07..a31db5e 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/InitializeEntityStatementContext.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/InitializeEntityStatementContext.java
@@ -29,6 +29,8 @@ import org.slf4j.Logger;
 import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
 
 import net.shibboleth.idp.plugin.oidc.op.metadata.resolver.ProviderMetadataResolver;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.impl.MetadataImpl;
 import net.shibboleth.idp.profile.AbstractProfileAction;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
@@ -167,7 +169,9 @@ public class InitializeEntityStatementContext extends AbstractProfileAction {
             return;
         }
         //TODO more types? (esp. OAuth2 AS)
-        entityStatementCtx.setMetadata(mergeMetadata(metadataSkeleton, "openid_provider", metadata.toJSONObject()));
+        final Map<String, Object> metadataMap = metadata.toJSONObject();
+        assert metadataSkeleton != null; assert metadataMap != null;
+        entityStatementCtx.setMetadata(mergeMetadata(metadataSkeleton, "openid_provider", metadataMap));
     }
 
     /**
@@ -179,7 +183,7 @@ public class InitializeEntityStatementContext extends AbstractProfileAction {
      * @param input the data to be merged (overwrites possibly already existing attributes)
      * @return the merged metadata
      */
-    protected Map<String,Map<String,Object>> mergeMetadata(@Nonnull final Map<String,Map<String,Object>> metadata,
+    protected Metadata mergeMetadata(@Nonnull final Map<String,Map<String,Object>> metadata,
             @Nonnull final String key, @Nonnull final Map<String,Object> input) {
         final Map<String,Map<String,Object>> result = new HashMap<>(metadata);
         final Map<String,Object> base = metadata.get(key);
@@ -190,6 +194,6 @@ public class InitializeEntityStatementContext extends AbstractProfileAction {
         } else {
             result.put(key, input);
         }
-        return result;
+        return new MetadataImpl(result);
     }
 }
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ResolveTrustChains.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ResolveTrustChains.java
index 2a0e538..788bb55 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ResolveTrustChains.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ResolveTrustChains.java
@@ -37,8 +37,8 @@ import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.SubjectEntityIDCriterion;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.RemoteTrustChainsContainer;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.SubjectEntityStatementCriterion;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.RelyingPartyTrustChainContext;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.VerifiedTrustChain;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultPreSelectedTrustChainIDsLookupStrategy;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultTrustChainIDsLookupStrategy;
 import net.shibboleth.oidc.metadata.cache.MetadataCache;
@@ -46,7 +46,6 @@ import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
 import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.collection.Pair;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.logic.FunctionSupport;
@@ -238,8 +237,7 @@ public class ResolveTrustChains extends AbstractTrustChainResolutionAction {
         final RelyingPartyTrustChainContext trustChainContext =
                 getTrustChainContextCreationStrategy().apply(profileRequestContext);
         trustChainContext.setResolvedTrustChains(cacheResult.get(0).getTrustChains());
-        final List<Pair<List<EntityStatement<?>>, Metadata>> policyCompliantChains =
-                new ArrayList<>();
+        final List<VerifiedTrustChain> policyCompliantChains = new ArrayList<>();
 
         String errorEventId = null;
         for (final List<EntityStatement<?>> chain : cacheResult.get(0).getTrustChains()) {
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ResolveTrustMarks.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ResolveTrustMarks.java
index 43bc044..19e31b4 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ResolveTrustMarks.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ResolveTrustMarks.java
@@ -44,8 +44,8 @@ import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.SubjectEntityIDCr
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.TrustMarkOwnersCriterion;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.RemoteTrustChainsContainer;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.SubjectEntityStatementCriterion;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.RelyingPartyTrustChainContext;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.VerifiedTrustChain;
 import net.shibboleth.idp.profile.AbstractProfileAction;
 import net.shibboleth.oidc.jwt.claims.ClaimsValidator;
 import net.shibboleth.oidc.jwt.claims.JWTValidationException;
@@ -54,7 +54,6 @@ import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
 import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.collection.Pair;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.logic.PredicateSupport;
@@ -291,22 +290,15 @@ public class ResolveTrustMarks extends AbstractProfileAction {
             return false;
         }
 
-        final Pair<List<EntityStatement<?>>, Metadata> selectedChain = trustChainContext.getSelectedTrustChain();
+        final VerifiedTrustChain selectedChain = trustChainContext.getSelectedTrustChain();
 
-        if (selectedChain == null || selectedChain.getFirst() == null) {
+        if (selectedChain == null) {
             ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
             log.error("{} No selected trust chain could be resolved", getLogPrefix());
             return false;
         }
 
-        selectedTrustChain = selectedChain.getFirst();
-        assert selectedTrustChain != null;
-        if (selectedTrustChain.size() < 3) {
-            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
-            log.error("{} Unexpected length in the selected trust chain: {}", getLogPrefix(),
-                    selectedTrustChain.size());
-            return false;
-        }
+        selectedTrustChain = selectedChain.getTrustChain();
 
         trustMarkClaimsValidator = trustMarkClaimsValidationLookupStrategy.apply(profileRequestContext);
         if (trustMarkClaimsValidator == null) {
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/SelectTrustChain.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/SelectTrustChain.java
index f831080..e6702d7 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/SelectTrustChain.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/SelectTrustChain.java
@@ -27,14 +27,13 @@ import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
 import org.slf4j.Logger;
 
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.RelyingPartyTrustChainContext;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.VerifiedTrustChain;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultTrustChainSelectionStrategy;
 import net.shibboleth.idp.profile.AbstractProfileAction;
 import net.shibboleth.profile.context.RelyingPartyContext;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.collection.Pair;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
@@ -59,7 +58,7 @@ public class SelectTrustChain extends AbstractProfileAction {
 
     /** Strategy used to fetch the selected trust chain and metadata. */
     @NonnullAfterInit
-    private Function<ProfileRequestContext,Pair<List<EntityStatement<?>>, Metadata>> selectedTrustChainLookupStrategy;
+    private Function<ProfileRequestContext,VerifiedTrustChain> selectedTrustChainLookupStrategy;
 
     /** Trust chain context to operate on. */
     @NonnullBeforeExec private RelyingPartyTrustChainContext trustChainContext;
@@ -111,7 +110,7 @@ public class SelectTrustChain extends AbstractProfileAction {
      * @param strategy lookup strategy
      */
     public void setSelectedTrustChainLookupStrategy(@Nonnull final
-            Function<ProfileRequestContext,Pair<List<EntityStatement<?>>, Metadata>> strategy) {
+            Function<ProfileRequestContext,VerifiedTrustChain> strategy) {
         checkSetterPreconditions();
         selectedTrustChainLookupStrategy =
                 Constraint.isNotNull(strategy, "SelectedTrustChainLookupStrategy cannot be null");
@@ -145,23 +144,22 @@ public class SelectTrustChain extends AbstractProfileAction {
     /** {@inheritDoc} */
     @Override
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-        final Pair<List<EntityStatement<?>>, Metadata> selectedChain =
-                selectedTrustChainLookupStrategy.apply(profileRequestContext);
+        final VerifiedTrustChain selectedChain = selectedTrustChainLookupStrategy.apply(profileRequestContext);
 
-        if (selectedChain == null || selectedChain.getFirst() == null || selectedChain.getSecond() == null) {
+        if (selectedChain == null) {
             ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
             log.error("{} No selected trust chain could be resolved", getLogPrefix());
             return;
         }
 
         final List<List<EntityStatement<?>>> rejectedTrustChains = trustChainContext.getRejectedTrustChains();
-        if (rejectedTrustChains != null && rejectedTrustChains.contains(selectedChain.getFirst())) {
+        if (rejectedTrustChains != null && rejectedTrustChains.contains(selectedChain.getTrustChain())) {
             ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
             log.error("{} The selected trust chain has been previously rejected", getLogPrefix());
             return;
         }
         trustChainContext.setSelectedTrustChains(selectedChain);
-        final List<EntityStatement<?>> selectedTrustChain = selectedChain.getFirst();
+        final List<EntityStatement<?>> selectedTrustChain = selectedChain.getTrustChain();
         assert selectedTrustChain != null;
         final RelyingPartyContext relyingPartyContext =
                 relyingPartyContextCreationStrategy.apply(profileRequestContext);
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/StoreAutomaticRegistration.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/StoreAutomaticRegistration.java
index 2a491a4..e1db4bf 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/StoreAutomaticRegistration.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/StoreAutomaticRegistration.java
@@ -15,7 +15,6 @@
 package net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl;
 
 import java.time.Instant;
-import java.util.List;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
@@ -31,16 +30,14 @@ import com.nimbusds.oauth2.sdk.id.ClientID;
 import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
 import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
 
-import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.RelyingPartyTrustChainContext;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.VerifiedTrustChain;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultSelectedTrustChainMetadataLookupStrategy;
 import net.shibboleth.idp.profile.AbstractProfileAction;
 import net.shibboleth.oidc.metadata.ClientInformationManager;
 import net.shibboleth.oidc.metadata.ClientInformationManagerException;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.collection.Pair;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
@@ -155,11 +152,11 @@ public class StoreAutomaticRegistration extends AbstractProfileAction {
             return false;
         }
 
-        final Pair<List<EntityStatement<?>>,Metadata> selectedTrustChain = trustChainContext.getSelectedTrustChain();
+        final VerifiedTrustChain selectedTrustChain = trustChainContext.getSelectedTrustChain();
         assert selectedTrustChain != null;
 
         clientInformation = new OIDCClientInformation(
-                new ClientID(selectedTrustChain.getFirst().get(0).getSubject()), metadata);
+                new ClientID(selectedTrustChain.getTrustChain().get(0).getSubject()), metadata);
 
         expiration = trustChainContext.getSelectedMetadataExpiration();
         if (expiration == null) {
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateAutomaticRegistrationProfileConfiguration.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateAutomaticRegistrationProfileConfiguration.java
index 2ba30d2..ce6d5ad 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateAutomaticRegistrationProfileConfiguration.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateAutomaticRegistrationProfileConfiguration.java
@@ -34,11 +34,11 @@ import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
 import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
 
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.impl.MetadataImpl;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.config.navigate.LocalMetadataPolicyLookupFunction;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.config.navigate.MandatoryTrustMarksLookupFunction;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.RelyingPartyTrustChainContext;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.VerifiedTrustChain;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultSelectedTrustChainMetadataLookupStrategy;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.support.ClientInformationExtensionSupport;
 import net.shibboleth.idp.profile.AbstractProfileAction;
@@ -48,7 +48,6 @@ import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.collection.Pair;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
 
@@ -91,7 +90,7 @@ public class ValidateAutomaticRegistrationProfileConfiguration extends AbstractP
     @NonnullBeforeExec private RelyingPartyTrustChainContext trustChainContext;
 
     /** Selected trust chain to operate on. */
-    @NonnullBeforeExec private Pair<List<EntityStatement<?>>, Metadata> selectedTrustChain;
+    @NonnullBeforeExec private VerifiedTrustChain selectedTrustChain;
 
     /**
      * Constructor.
@@ -182,8 +181,7 @@ public class ValidateAutomaticRegistrationProfileConfiguration extends AbstractP
         }
 
         selectedTrustChain = trustChainContext.getSelectedTrustChain();
-        assert selectedTrustChain != null;
-        if (selectedTrustChain.getFirst() == null || selectedTrustChain.getSecond() == null) {
+        if (selectedTrustChain == null) {
             log.error("{} Selected trust chain contents is not populated", getLogPrefix());
             ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
             return false;
@@ -208,7 +206,7 @@ public class ValidateAutomaticRegistrationProfileConfiguration extends AbstractP
             log.debug("{} Applying local metadata policy into the client metadata", getLogPrefix());
             clientInformation = localMetadataPolicyMergingStrategy.apply(
                     new OIDCClientInformation(
-                            new ClientID(selectedTrustChain.getFirst().get(0).getSubject()),
+                            new ClientID(selectedTrustChain.getTrustChain().get(0).getSubject()),
                             metadata), localMetadataPolicy);
             if (clientInformation == null) {
                 log.error("{} Could not apply the local metadata policy into the client metadata", getLogPrefix());
@@ -217,10 +215,10 @@ public class ValidateAutomaticRegistrationProfileConfiguration extends AbstractP
             }
             final MetadataImpl selectedMetadata = new MetadataImpl();
             selectedMetadata.setOpenidRelyingPartyMetadata(clientInformation.toJSONObject());
-            selectedTrustChain.setSecond(selectedMetadata);
+            selectedTrustChain.setMetadata(selectedMetadata);
         } else {
             clientInformation = new OIDCClientInformation(
-                    new ClientID(selectedTrustChain.getFirst().get(0).getSubject()), metadata);
+                    new ClientID(selectedTrustChain.getTrustChain().get(0).getSubject()), metadata);
 
         }
         final String clientId = clientInformation.getID().getValue();
@@ -234,10 +232,10 @@ public class ValidateAutomaticRegistrationProfileConfiguration extends AbstractP
                         getLogPrefix(), mandatoryTrustMarks);
                 final List<List<EntityStatement<?>>> rejectedTrustChains = trustChainContext.getRejectedTrustChains();
                 if (rejectedTrustChains == null) {
-                    trustChainContext.setRejectedTrustChains(List.of(selectedTrustChain.getFirst()));
+                    trustChainContext.setRejectedTrustChains(List.of(selectedTrustChain.getTrustChain()));
                 } else {
                     final List<List<EntityStatement<?>>> rejectedChains = new ArrayList<>(rejectedTrustChains);
-                    rejectedChains.add(selectedTrustChain.getFirst());
+                    rejectedChains.add(selectedTrustChain.getTrustChain());
                     trustChainContext.setRejectedTrustChains(CollectionSupport.copyToList(rejectedChains));
                 }
                 ActionSupport.buildEvent(profileRequestContext, RESELECT_TRUST_CHAIN);
@@ -245,7 +243,7 @@ public class ValidateAutomaticRegistrationProfileConfiguration extends AbstractP
             }
         }
 
-        final List<EntityStatement<?>> trustChain = selectedTrustChain.getFirst();
+        final List<EntityStatement<?>> trustChain = selectedTrustChain.getTrustChain();
         assert trustChain != null;
         trustChainContext.setSelectedMetadataExpiration(resolveTrustChainExpiration(trustChain));
 
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateProvidedEntityConfiguration.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateProvidedEntityConfiguration.java
index d791eac..aaf7b95 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateProvidedEntityConfiguration.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateProvidedEntityConfiguration.java
@@ -48,14 +48,14 @@ public class ValidateProvidedEntityConfiguration extends AbstractProfileAction {
 
     /** Strategy used to validate provided trust chain. */
     @NonnullAfterInit
-    private BiPredicate<ProfileRequestContext, EntityStatement> providedEntityConfigurationValidationStrategy;
+    private BiPredicate<ProfileRequestContext, EntityStatement<?>> providedEntityConfigurationValidationStrategy;
 
     /** Strategy used to locate the provided trust chain. */
     @NonnullAfterInit
-    private Function<ProfileRequestContext, EntityStatement> providedEntityConfigurationLookupStrategy;
+    private Function<ProfileRequestContext, EntityStatement<?>> providedEntityConfigurationLookupStrategy;
 
     /** Entity configuration to operate on. */
-    @NonnullBeforeExec private EntityStatement entityConfiguration;
+    @NonnullBeforeExec private EntityStatement<?> entityConfiguration;
 
     /**
      * Set the strategy used to locate the provided entity configuration.
@@ -63,7 +63,7 @@ public class ValidateProvidedEntityConfiguration extends AbstractProfileAction {
      * @param strategy lookup strategy
      */
     public void setProvidedEntityConfigurationLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext, EntityStatement> strategy) {
+            @Nonnull final Function<ProfileRequestContext, EntityStatement<?>> strategy) {
         providedEntityConfigurationLookupStrategy =
                 Constraint.isNotNull(strategy, "ProvidedEntityConfigurationLookupStrategy cannot be null");
     }
@@ -74,7 +74,7 @@ public class ValidateProvidedEntityConfiguration extends AbstractProfileAction {
      * @param strategy validation strategy
      */
     public void setProvidedEntityConfigurationValidationStrategy(
-            @Nonnull final BiPredicate<ProfileRequestContext, EntityStatement> strategy) {
+            @Nonnull final BiPredicate<ProfileRequestContext, EntityStatement<?>> strategy) {
         checkSetterPreconditions();
         providedEntityConfigurationValidationStrategy =
                 Constraint.isNotNull(strategy, "ProvidedEntityConfigurationValidationStrategy cannot be null");
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateProvidedTrustChain.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateProvidedTrustChain.java
index b368c1c..2ecef8f 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateProvidedTrustChain.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateProvidedTrustChain.java
@@ -26,12 +26,11 @@ import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.RelyingPartyTrustChainContext;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.VerifiedTrustChain;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
 import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.collection.Pair;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
@@ -55,7 +54,8 @@ public class ValidateProvidedTrustChain extends AbstractTrustChainResolutionActi
     private BiPredicate<ProfileRequestContext, List<EntityStatement<?>>> providedTrustChainValidationStrategy;
 
     /** Strategy used to locate the provided trust chain. */
-    @NonnullAfterInit private Function<ProfileRequestContext, List<EntityStatement<?>>> providedTrustChainLookupStrategy;
+    @NonnullAfterInit
+    private Function<ProfileRequestContext, List<EntityStatement<?>>> providedTrustChainLookupStrategy;
 
     /** Trust chain to operate on. */
     @NonnullBeforeExec private List<EntityStatement<?>> trustChain;
@@ -124,9 +124,9 @@ public class ValidateProvidedTrustChain extends AbstractTrustChainResolutionActi
                 getTrustChainContextCreationStrategy().apply(profileRequestContext);
         assert trustChain != null;
         trustChainContext.setResolvedTrustChains(CollectionSupport.listOf(trustChain));
-        final List<Pair<List<EntityStatement<?>>, Metadata>> policyCompliantChains =
-                new ArrayList<>();
+        final List<VerifiedTrustChain> policyCompliantChains = new ArrayList<>();
 
+        assert trustChain != null;
         final String errorEventId = populatePolicyComplaintChains(trustChain, policyCompliantChains);
         if (errorEventId != null) {
             log.error("{} The request metadata is not compliant with the merged policy", getLogPrefix());
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateResolveEntityRequest.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateResolveEntityRequest.java
index 06c53a4..1f80655 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateResolveEntityRequest.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateResolveEntityRequest.java
@@ -172,7 +172,7 @@ public class ValidateResolveEntityRequest extends AbstractProfileAction {
         final SubjectEntityIDCriterion criterion = new SubjectEntityIDCriterion(candidate);
         try {
             final List<Map<String,LocalKeyContainer>> result = localTrustAnchorsCache.get(new CriteriaSet(criterion));
-            if (result == null || result.isEmpty() || result.get(0).isEmpty()) {
+            if (result.isEmpty() || result.get(0).isEmpty()) {
                 log.debug("{} No locally trusted keys found for {}", getLogPrefix(), candidate);
                 return false;
             }
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateSelectedTrustChain.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateSelectedTrustChain.java
index 841a275..c734cdd 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateSelectedTrustChain.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateSelectedTrustChain.java
@@ -29,15 +29,14 @@ import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.ResolveEntityRequ
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
 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.oidfed.metadata.payload.claim.Metadata;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.RelyingPartyTrustChainContext;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.VerifiedTrustChain;
 import net.shibboleth.idp.profile.AbstractProfileAction;
 import net.shibboleth.oidc.metadata.cache.MetadataCache;
 import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
 import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.collection.Pair;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
@@ -166,10 +165,10 @@ public class ValidateSelectedTrustChain extends AbstractProfileAction {
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
         final RelyingPartyTrustChainContext trustChainContext =
                 trustChainContextLookupStrategy.apply(profileRequestContext);
-        final Pair<List<EntityStatement<?>>, Metadata>  selectedTrustChain =
+        final VerifiedTrustChain selectedTrustChain =
                 trustChainContext != null ? trustChainContext.getSelectedTrustChain() : null;
-        if (selectedTrustChain == null || selectedTrustChain.getFirst() == null) {
-            final List<Pair<List<EntityStatement<?>>,Metadata>> allChains =
+        if (selectedTrustChain == null) {
+            final List<VerifiedTrustChain> allChains =
                     trustChainContext != null ? trustChainContext.getPolicyCompliantTrustChains() : null;
             if (allChains == null || allChains.isEmpty()) {
                 if (isSubjectValid(validatedRequest.getSubject())) {
@@ -189,7 +188,7 @@ public class ValidateSelectedTrustChain extends AbstractProfileAction {
         }
         
         final List<String> trustAnchors = validatedRequest.getTrustAnchors();
-        final List<EntityStatement<?>> candidateChain = selectedTrustChain.getFirst();
+        final List<EntityStatement<?>> candidateChain = selectedTrustChain.getTrustChain();
         assert candidateChain != null;
         final String candidateAnchor = candidateChain.get(candidateChain.size() - 1).getSubject();
         if (!trustAnchors.contains(candidateAnchor)) {
@@ -198,10 +197,10 @@ public class ValidateSelectedTrustChain extends AbstractProfileAction {
             assert trustChainContext != null;
             final List<List<EntityStatement<?>>> rejectedTrustChains = trustChainContext.getRejectedTrustChains();
             if (rejectedTrustChains == null) {
-                trustChainContext.setRejectedTrustChains(List.of(selectedTrustChain.getFirst()));
+                trustChainContext.setRejectedTrustChains(List.of(selectedTrustChain.getTrustChain()));
             } else {
                 final List<List<EntityStatement<?>>> rejectedChains = new ArrayList<>(rejectedTrustChains);
-                rejectedChains.add(selectedTrustChain.getFirst());
+                rejectedChains.add(selectedTrustChain.getTrustChain());
                 trustChainContext.setRejectedTrustChains(CollectionSupport.copyToList(rejectedChains));
             }
             ActionSupport.buildEvent(profileRequestContext, OidFederationEventIds.RESELECT_TRUST_CHAIN);
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultPreSelectedTrustChainIDsLookupStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultPreSelectedTrustChainIDsLookupStrategy.java
index 56ec368..4ed4ff6 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultPreSelectedTrustChainIDsLookupStrategy.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultPreSelectedTrustChainIDsLookupStrategy.java
@@ -83,9 +83,6 @@ public class DefaultPreSelectedTrustChainIDsLookupStrategy extends AbstractIdent
         if (profileIdLookupStrategy == null) {
             throw new ComponentInitializationException("ProfileIdLookupStrategy cannot be null");
         }
-        if (profileSpecificStrategies == null) {
-            throw new ComponentInitializationException("ProfileSpecificStrategies cannot be null");
-        }
     }
 
     /** {@inheritDoc} */
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainIDsLookupStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainIDsLookupStrategy.java
index 9724d24..b60a7a9 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainIDsLookupStrategy.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainIDsLookupStrategy.java
@@ -56,7 +56,7 @@ public class DefaultSelectedTrustChainIDsLookupStrategy extends AbstractTrustCha
     @Override @Nullable
     public List<String> doApply(@Nonnull final RelyingPartyTrustChainContext trustChainContext) {
         return Optional.ofNullable(trustChainContext.getSelectedTrustChain())
-                .map(pair -> pair.getFirst())
+                .map(verifiedChain -> verifiedChain.getTrustChain())
                 .map(chain -> trustChainIDsLookupStrategy.apply(chain))
                 .orElse(null);
     }
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainImmediateSuperiorLookupStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainImmediateSuperiorLookupStrategy.java
index 31e953c..7cecb66 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainImmediateSuperiorLookupStrategy.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainImmediateSuperiorLookupStrategy.java
@@ -32,7 +32,7 @@ public class DefaultSelectedTrustChainImmediateSuperiorLookupStrategy
     @Override @Nullable
     public String doApply(@Nonnull final RelyingPartyTrustChainContext trustChainContext) {
         return Optional.ofNullable(trustChainContext.getSelectedTrustChain())
-                .map(pair -> pair.getFirst())
+                .map(verifiedChain -> verifiedChain.getTrustChain())
                 .filter(list -> list.size() >= 3)
                 .map(list -> list.get(1))
                 .map(entityStatement -> entityStatement.getIssuer())
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainMetadataLookupStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainMetadataLookupStrategy.java
index b3f2276..604c629 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainMetadataLookupStrategy.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainMetadataLookupStrategy.java
@@ -36,7 +36,7 @@ public class DefaultSelectedTrustChainMetadataLookupStrategy
     @Override @Nullable
     public OIDCClientMetadata doApply(@Nonnull final RelyingPartyTrustChainContext trustChainContext) {
         return Optional.ofNullable(trustChainContext.getSelectedTrustChain())
-                .map(pair -> pair.getSecond())
+                .map(verifiedChain -> verifiedChain.getMetadata())
                 .map(map -> map.getOpenidRelyingPartyMetadata())
                 .map(JSONObject::new)
                 .map(json-> {
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainTrustAnchorLookupStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainTrustAnchorLookupStrategy.java
index 6ac7900..83e2b0c 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainTrustAnchorLookupStrategy.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainTrustAnchorLookupStrategy.java
@@ -32,7 +32,7 @@ public class DefaultSelectedTrustChainTrustAnchorLookupStrategy
     @Override @Nullable
     public String doApply(@Nonnull final RelyingPartyTrustChainContext trustChainContext) {
         return Optional.ofNullable(trustChainContext.getSelectedTrustChain())
-                .map(pair -> pair.getFirst())
+                .map(verifiedChain -> verifiedChain.getTrustChain())
                 .filter(list -> list.size() >= 3)
                 .map(list -> list.get(list.size() - 1))
                 .map(entityStatement -> entityStatement.getSubject())
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustAnchorHintsLookupStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustAnchorHintsLookupStrategy.java
index 8b2c146..a46383d 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustAnchorHintsLookupStrategy.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustAnchorHintsLookupStrategy.java
@@ -80,7 +80,7 @@ public class DefaultTrustAnchorHintsLookupStrategy extends AbstractIdentifiableI
             log.warn("Could not resolve any trust anchors", e);
             return null;
         }
-        if (keyContainers == null || keyContainers.isEmpty()) {
+        if (keyContainers.isEmpty()) {
             log.debug("No keycontainers returned from the trust anchor cache");
             return null;
         }
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustChainMetadataPolicyMergingStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustChainMetadataPolicyMergingStrategy.java
index 4c968d8..3a03509 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustChainMetadataPolicyMergingStrategy.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustChainMetadataPolicyMergingStrategy.java
@@ -42,14 +42,14 @@ import net.shibboleth.shared.primitive.StringSupport;
  */
 @ThreadSafeAfterInit
 public class DefaultTrustChainMetadataPolicyMergingStrategy extends AbstractIdentifiableInitializableComponent
-        implements BiFunction<List<EntityStatement>,String, Map<String, MetadataPolicy>> {
+        implements BiFunction<List<EntityStatement<?>>,String, Map<String, MetadataPolicy>> {
 
     /** The strategy used for merging two metadata policies. */
     @NonnullAfterInit private BiFunction<Map<String, MetadataPolicy>, Map<String, MetadataPolicy>,
         Pair<Map<String, MetadataPolicy>, Boolean>> metadataPolicyMergingStrategy;
 
     /** The strategy used for local (additional) metadata policy. */
-    @NonnullAfterInit private Function<List<EntityStatement>, Map<String, MetadataPolicy>>
+    @NonnullAfterInit private Function<List<EntityStatement<?>>, Map<String, MetadataPolicy>>
         localMetadataPolicyStrategy;
 
     /**
@@ -70,7 +70,7 @@ public class DefaultTrustChainMetadataPolicyMergingStrategy extends AbstractIden
      * @param strategy What to set.
      */
     public void setLocalMetadataPolicyStrategy(
-            @Nonnull final Function<List<EntityStatement>, Map<String, MetadataPolicy>> strategy) {
+            @Nonnull final Function<List<EntityStatement<?>>, Map<String, MetadataPolicy>> strategy) {
         checkSetterPreconditions();
         localMetadataPolicyStrategy = Constraint.isNotNull(strategy,
                 "Local metadata policy strategy cannot be null");
@@ -90,7 +90,7 @@ public class DefaultTrustChainMetadataPolicyMergingStrategy extends AbstractIden
 
     /** {@inheritDoc} */
     @Override @Nonnull
-    public Map<String, MetadataPolicy> apply(@Nullable final List<EntityStatement> trustChain,
+    public Map<String, MetadataPolicy> apply(@Nullable final List<EntityStatement<?>> trustChain,
             @Nullable final String entityType) throws ConstraintViolationException {
         checkComponentActive();
         if (trustChain == null || trustChain.isEmpty()) {
@@ -98,7 +98,7 @@ public class DefaultTrustChainMetadataPolicyMergingStrategy extends AbstractIden
         }
         final Map<String, MetadataPolicy> result = new HashMap<>();
         for (int i = trustChain.size(); i > 0; i--) {
-            final EntityStatement entityStatement = trustChain.get(i - 1);
+            final EntityStatement<?> entityStatement = trustChain.get(i - 1);
             if (entityStatement.getParsedPayload() instanceof SubordinateStatementPayload ssp) {
                 final Map<String, Map<String, MetadataPolicy>> policy = ssp.getMetadataPolicy();
                 if (policy == null || policy.isEmpty()) {
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustChainSelectionStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustChainSelectionStrategy.java
index 6e94022..3286716 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustChainSelectionStrategy.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustChainSelectionStrategy.java
@@ -22,9 +22,8 @@ import javax.annotation.Nullable;
 import org.slf4j.Logger;
 
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.claim.Metadata;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.RelyingPartyTrustChainContext;
-import net.shibboleth.shared.collection.Pair;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.VerifiedTrustChain;
 import net.shibboleth.shared.primitive.LoggerFactory;
 
 /**
@@ -33,16 +32,16 @@ import net.shibboleth.shared.primitive.LoggerFactory;
  * {@link RelyingPartyTrustChainContext#getRejectedTrustChains()}.
  */
 public class DefaultTrustChainSelectionStrategy
-    extends AbstractTrustChainContextLookupFunction<Pair<List<EntityStatement<?>>, Metadata>> {
+    extends AbstractTrustChainContextLookupFunction<VerifiedTrustChain> {
 
     /** Class logger. */
     @Nonnull private Logger log = LoggerFactory.getLogger(DefaultTrustChainSelectionStrategy.class);
 
     /** {@inheritDoc} */
     @Override @Nullable
-    public Pair<List<EntityStatement<?>>, Metadata> doApply(
+    public VerifiedTrustChain doApply(
             @Nonnull final RelyingPartyTrustChainContext trustChainContext) {
-        final List<Pair<List<EntityStatement<?>>, Metadata>> policyCompliantChains =
+        final List<VerifiedTrustChain> policyCompliantChains =
                 trustChainContext.getPolicyCompliantTrustChains();
 
         if (policyCompliantChains == null || policyCompliantChains.isEmpty()) {
@@ -54,15 +53,15 @@ public class DefaultTrustChainSelectionStrategy
         if (policyCompliantChains.size() > 1) {
             int shortestIndex = -1;
             for (int i = 0; i < policyCompliantChains.size(); i++) {
-                final List<EntityStatement<?>> candidate = policyCompliantChains.get(i).getFirst();
+                final List<EntityStatement<?>> candidate = policyCompliantChains.get(i).getTrustChain();
                 if (isTrustChainRejected(trustChainContext, candidate)) {
                     continue;
                 }
                 if (shortestIndex == -1) {
                     shortestIndex = i;
                 } else {
-                    final List<EntityStatement<?>> shortest = policyCompliantChains.get(shortestIndex).getFirst();
-                    if (candidate != null && shortest != null && candidate.size() < shortest.size()) {
+                    final List<EntityStatement<?>> shortest = policyCompliantChains.get(shortestIndex).getTrustChain();
+                    if (candidate.size() < shortest.size()) {
                         shortestIndex = i;
                     }
                 }
@@ -70,7 +69,7 @@ public class DefaultTrustChainSelectionStrategy
             log.trace("Shortest non-rejected index {}", shortestIndex);
             return shortestIndex == -1 ? null : policyCompliantChains.get(shortestIndex);
         }
-        final List<EntityStatement<?>> candidate = policyCompliantChains.get(0).getFirst();
+        final List<EntityStatement<?>> candidate = policyCompliantChains.get(0).getTrustChain();
         return isTrustChainRejected(trustChainContext, candidate) ? null : policyCompliantChains.get(0);
     }
 
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/ExplicitClientRegistrationRequestEntityConfigurationLookupFunction.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/ExplicitClientRegistrationRequestEntityConfigurationLookupFunction.java
index 8a5ae35..eec79c7 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/ExplicitClientRegistrationRequestEntityConfigurationLookupFunction.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/ExplicitClientRegistrationRequestEntityConfigurationLookupFunction.java
@@ -30,11 +30,11 @@ import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
  */
 @ThreadSafe
 public class ExplicitClientRegistrationRequestEntityConfigurationLookupFunction
-    implements ContextDataLookupFunction<ProfileRequestContext, EntityStatement> {
+    implements ContextDataLookupFunction<ProfileRequestContext, EntityStatement<?>> {
 
     /** {@inheritDoc} */
     @Nullable
-    public EntityStatement apply(@Nullable final ProfileRequestContext input) {
+    public EntityStatement<?> apply(@Nullable final ProfileRequestContext input) {
         return Optional.ofNullable(input)
                 .map(profileRequestContext -> profileRequestContext.getInboundMessageContext())
                 .map(messageContext -> messageContext.getMessage())
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultEntityConfigurationCredentialResolver.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultEntityConfigurationCredentialResolver.java
index e29d84b..73a1834 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultEntityConfigurationCredentialResolver.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultEntityConfigurationCredentialResolver.java
@@ -14,6 +14,7 @@
 
 package net.shibboleth.idp.plugin.oidc.op.oidfed.security.credential;
 
+import java.util.List;
 import java.util.Objects;
 
 import javax.annotation.Nonnull;
@@ -48,12 +49,13 @@ public class DefaultEntityConfigurationCredentialResolver extends BasicJOSEObjec
             throw new ResolverException("No criteria set supplied");
         }
 
-        return parseJwkSet(criteriaSet).getKeys().stream()
-                .filter(Objects::nonNull)
-                .map(jwk -> buildJWKCredential(jwk, null))
+        final List<Credential> result = parseJwkSet(criteriaSet).getKeys().stream()
+                .map(jwk -> jwk != null ? buildJWKCredential(jwk, null) : null)
                 .filter(Objects::nonNull)
                 .map(Credential.class::cast)
                 .toList();
+        assert result != null;
+        return result;
     }
 
     /**
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultPayloadJOSEObjectCredentialResolver.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultPayloadJOSEObjectCredentialResolver.java
index 2cf1327..86956ad 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultPayloadJOSEObjectCredentialResolver.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultPayloadJOSEObjectCredentialResolver.java
@@ -15,6 +15,7 @@
 package net.shibboleth.idp.plugin.oidc.op.oidfed.security.credential;
 
 import java.text.ParseException;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
@@ -65,14 +66,16 @@ public class DefaultPayloadJOSEObjectCredentialResolver extends BasicJOSEObjectC
                 return CollectionSupport.emptyList();
             }
             final JWKSet jwks = JWKSet.parse(rawJwks);
-            return jwks.getKeys().stream()
+            final List<Credential> result = jwks.getKeys().stream()
                     .filter(Objects::nonNull)
                     .filter(jwk -> jwk.getAlgorithm() != null ? 
                             jwk.getAlgorithm().equals(jwt.getHeader().getAlgorithm()) : true)
-                    .map(jwk -> buildJWKCredential(jwk, null))
+                    .map(jwk -> jwk != null ? buildJWKCredential(jwk, null) : null)
                     .filter(Objects::nonNull)
                     .map(Credential.class::cast)
                     .toList();
+            assert result != null;
+            return result;
         } catch (final ParseException e) {
             throw new ResolverException("Could not parse JWKSet from JOSEObject", e);
         }
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultPayloadSignatureValidationCredentialResolver.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultPayloadSignatureValidationCredentialResolver.java
index 310fec9..8caead7 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultPayloadSignatureValidationCredentialResolver.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultPayloadSignatureValidationCredentialResolver.java
@@ -15,6 +15,7 @@
 package net.shibboleth.idp.plugin.oidc.op.oidfed.security.credential;
 
 import java.text.ParseException;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
@@ -66,12 +67,13 @@ public class DefaultPayloadSignatureValidationCredentialResolver extends BasicJO
                 return CollectionSupport.emptyList();
             }
             final JWKSet jwks = JWKSet.parse(rawJwks);
-            return jwks.getKeys().stream()
-                    .filter(Objects::nonNull)
-                    .map(jwk -> buildJWKCredential(jwk, null))
+            final List<Credential> result =  jwks.getKeys().stream()
+                    .map(jwk -> jwk != null ? buildJWKCredential(jwk, null) : null)
                     .filter(Objects::nonNull)
                     .map(Credential.class::cast)
                     .toList();
+            assert result != null;
+            return result;
         } catch (final ParseException e) {
             throw new ResolverException("Could not parse JWKSet from JOSEObject", e);
         }
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultSubordinateStatementCredentialResolver.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultSubordinateStatementCredentialResolver.java
index feb3bfb..afafd7b 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultSubordinateStatementCredentialResolver.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/credential/DefaultSubordinateStatementCredentialResolver.java
@@ -14,6 +14,7 @@
 
 package net.shibboleth.idp.plugin.oidc.op.oidfed.security.credential;
 
+import java.util.List;
 import java.util.Objects;
 
 import javax.annotation.Nonnull;
@@ -51,12 +52,14 @@ public class DefaultSubordinateStatementCredentialResolver extends BasicJOSEObje
             throw new ResolverException("No criteria set supplied");
         }
 
-        return parseJwkSet(criteriaSet).getKeys().stream()
+        final List<Credential> result =  parseJwkSet(criteriaSet).getKeys().stream()
                 .filter(Objects::nonNull)
-                .map(jwk -> buildJWKCredential(jwk, null))
+                .map(jwk -> jwk != null ? buildJWKCredential(jwk, null) : null)
                 .filter(Objects::nonNull)
                 .map(Credential.class::cast)
                 .toList();
+        assert result != null;
+        return result;
     }
 
     /**
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 062e1d4..95b4a1e 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
@@ -320,8 +320,7 @@
                 p:entityConfigurationCache-ref="shibboleth.oidfed.EntityConfigurationMetadataCache"
                 p:subordinateStatementCache-ref="shibboleth.oidfed.SubordinateEntityStatementMetadataCache"
                 p:localTrustAnchorsCache-ref="shibboleth.oidfed.LocalTrustAnchorsMetadataCache"
-                p:federationPolicyConstraints-ref="%{idp.oidfed.FederationPolicyConstraints:shibboleth.oidfed.DefaultFederationPolicyConstraints}"
-                p:objectMapper-ref="shibboleth.oidfed.JWTPayloadJSONObjectMapper">
+                p:federationPolicyConstraints-ref="%{idp.oidfed.FederationPolicyConstraints:shibboleth.oidfed.DefaultFederationPolicyConstraints}">
                 <property name="criteriaToValidContainerLifetimeStrategy">
                     <bean parent="shibboleth.Functions.Constant">
                         <constructor-arg>
@@ -1002,7 +1001,6 @@
     <bean id="shibboleth.oidfed.RemoteTrustMark" abstract="true"
         class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.DefaultTrustMarkFromMetadataCacheFetchingFunction"
         p:subject-ref="shibboleth.oidc.issuer"
-        p:objectMapper-ref="shibboleth.oidc.JSONObjectMapper"
         p:entityConfigurationCache-ref="shibboleth.oidfed.EntityConfigurationMetadataCache"
         p:trustMarkCache-ref="shibboleth.oidfed.TrustMarkMetadataCache"/>
 
diff --git a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-beans.xml b/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-beans.xml
index 6c1e6fa..758ac8f 100644
--- a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-beans.xml
+++ b/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-beans.xml
@@ -46,8 +46,7 @@
         p:metadataValidationCondition-ref="#{'%{idp.oidfed.MetadataValidationCondition:DefaultMetadataValidationCondition}'.trim()}">
         <property name="providedTrustChainValidationStrategy">
             <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.DefaultProvidedTrustChainValidationStrategy"
-                p:federationPolicyConstraints-ref="%{idp.oidfed.FederationPolicyConstraints:shibboleth.oidfed.DefaultFederationPolicyConstraints}"
-                p:objectMapper-ref="shibboleth.oidfed.JWTPayloadJSONObjectMapper">
+                p:federationPolicyConstraints-ref="%{idp.oidfed.FederationPolicyConstraints:shibboleth.oidfed.DefaultFederationPolicyConstraints}">
                 <property name="trustChainSignatureValidationFilterStrategy">
                     <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.DefaultProvidedTrustChainSignatureValidationFilterStrategy">
                         <property name="trustEngine">
@@ -150,8 +149,7 @@
                 p:entityConfigurationCache-ref="shibboleth.oidfed.EntityConfigurationMetadataCache"
                 p:subordinateStatementCache-ref="shibboleth.oidfed.SubordinateEntityStatementMetadataCache"
                 p:localTrustAnchorsCache-ref="shibboleth.oidfed.LocalTrustAnchorsMetadataCache"
-                p:federationPolicyConstraints-ref="%{idp.oidfed.FederationPolicyConstraints:shibboleth.oidfed.DefaultFederationPolicyConstraints}"
-                p:objectMapper-ref="shibboleth.oidfed.JWTPayloadJSONObjectMapper">
+                p:federationPolicyConstraints-ref="%{idp.oidfed.FederationPolicyConstraints:shibboleth.oidfed.DefaultFederationPolicyConstraints}">
                 <property name="criteriaToValidContainerLifetimeStrategy">
                     <bean parent="shibboleth.Functions.Constant">
                         <constructor-arg>

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


More information about the commits mailing list