[java-idp-plugin-oidc-op-oidfed] 03/03: Iniitial version of the hook to wire additional claims into entity configuration

Codeberg noreply at shibboleth.net
Thu Dec 4 07:24:06 UTC 2025


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

codeberg pushed a commit to branch main
in repository java-idp-plugin-oidc-op-oidfed.

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

commit 1f0a94820d5265ece914af8685445977b678514e
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Thu Dec 4 09:23:25 2025 +0200

    Iniitial version of the hook to wire additional claims into entity configuration
    
    - shibboleth.oidfed.EntityConfigurationClaimLookupStrategies bean in conf/oidfed/oidfed-entity-configuration-claims.xml
      - Type Map<String, Function<ProfileRequestContext,Object>> optionalClaimsLookupStrategies
    - shibboleth.oidfed.RemoteTrustMark abstract bean can be used for fetching trust marks via remote API
      - Trust Marks are stored in shibboleth.oidfed.TrustMarkMetadataCache
    - Tests contain example of wiring trust mark both statically and dynamically via API
---
 ...esolverEntity.java => TrustedRemoteEntity.java} |  37 +---
 .../profile/TrustedRemoteResolverEntity.java       |  18 +-
 .../op/oidfed/messaging/impl/TrustMarkRequest.java | 113 ++++++++++++
 .../oidfed/messaging/impl/TrustMarkResponse.java   |  87 +++++++++
 .../metadata/DefaultTrustMarkFetchingStrategy.java | 114 ++++++++++++
 ...arkResponseContainerExpirationTimeStrategy.java |  59 ++++++
 ...kResponseSignatureValidationFilterStrategy.java | 128 +++++++++++++
 .../oidfed/metadata/TrustMarkRequestCriterion.java |  79 ++++++++
 .../metadata/TrustMarkResponseContainer.java       |  75 ++++++++
 .../profile/impl/BuildEntityConfiguration.java     |  33 +++-
 ...TrustMarkFromMetadataCacheFetchingFunction.java | 204 +++++++++++++++++++++
 ...ntityConfigurationTrustMarksLookupStrategy.java |  77 ++++++++
 .../META-INF/net.shibboleth.idp/postconfig.xml     |  41 +++++
 .../entity-configuration-beans.xml                 |   3 +-
 .../oidfed/oidfed-entity-configuration-claims.xml  |  17 ++
 .../idp/plugin/oidc/op/oidfed/module.properties    |   4 +
 .../flow/oidfed/AbstractFederationFlowTest.java    |   5 +-
 .../flow/oidfed/EntityConfigurationFlowTest.java   |  49 +++++
 .../oidfed/oidfed-entity-configuration-claims.xml  |  43 +++++
 19 files changed, 1139 insertions(+), 47 deletions(-)

diff --git a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteEntity.java
similarity index 54%
copy from idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java
copy to idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteEntity.java
index 005dd3a..624ed2e 100644
--- a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java
+++ b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteEntity.java
@@ -14,8 +14,6 @@
 
 package net.shibboleth.idp.plugin.oidc.op.oidfed.profile;
 
-import java.util.Collection;
-
 import javax.annotation.Nonnull;
 
 import com.google.common.base.MoreObjects;
@@ -25,52 +23,36 @@ import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.logic.Constraint;
 
 /**
- * A trusted entity whose federation_resolve_endpoint is exploited.
+ * A trusted remote entity whose federation endpoints are exploited.
  */
-public class TrustedRemoteResolverEntity {
+public class TrustedRemoteEntity {
 
-    /** The entity ID of the remote resolver. */
+    /** The entity ID of the remote entity. */
     @Nonnull @NotEmpty private final String entityId;
 
-    /** The trust anchors to be used within the API request. */
-    @Nonnull @NotEmpty private final Collection<String> trustAnchors;
-
     /**
      * Constructor.
      *
-     * @param entity ntity ID of the remote resolver
-     * @param anchors trust anchors to be used within the API request
+     * @param entity entity ID of the remote entity
      */
-    public TrustedRemoteResolverEntity(@Nonnull @NotEmpty @ParameterName(name="entity") final String entity,
-            @Nonnull @NotEmpty @ParameterName(name="anchors") final Collection<String> anchors) {
+    public TrustedRemoteEntity(@Nonnull @NotEmpty @ParameterName(name="entity") final String entity) {
         entityId = Constraint.isNotEmpty(entity, "Entity ID cannot be empty");
-        trustAnchors = Constraint.isNotEmpty(anchors, "Trust Anchors cannot be empty");
     }
 
     /**
-     * Get the entity ID of the remote resolver.
+     * Get the entity ID of the remote entity.
      * 
-     * @return entity ID of the remote resolver
+     * @return entity ID of the remote entity
      */
     @Nonnull @NotEmpty public String getEntityId() {
         return entityId;
     }
 
-    /**
-     * Get the trust anchors to be used within the API request.
-     * 
-     * @return trust anchors to be used within the API request
-     */
-    @Nonnull @NotEmpty public Collection<String> getTrustAnchors() {
-        return trustAnchors;
-    }
-
     /** {@inheritDoc} */
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(this)
                 .add("entityId", getEntityId())
-                .add("trustAnchors", getTrustAnchors())
                 .toString();
     }
 
@@ -86,8 +68,7 @@ public class TrustedRemoteResolverEntity {
         if (getClass() != obj.getClass()) {
             return false;
         }
-        final TrustedRemoteResolverEntity other = (TrustedRemoteResolverEntity) obj;
-        return entityId.equals(other.entityId) &&
-                trustAnchors.containsAll(other.trustAnchors) && other.trustAnchors.containsAll(trustAnchors);
+        final TrustedRemoteEntity other = (TrustedRemoteEntity) obj;
+        return entityId.equals(other.entityId);
     }
 }
diff --git a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java
index 005dd3a..4871224 100644
--- a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java
+++ b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java
@@ -27,10 +27,7 @@ import net.shibboleth.shared.logic.Constraint;
 /**
  * A trusted entity whose federation_resolve_endpoint is exploited.
  */
-public class TrustedRemoteResolverEntity {
-
-    /** The entity ID of the remote resolver. */
-    @Nonnull @NotEmpty private final String entityId;
+public class TrustedRemoteResolverEntity extends TrustedRemoteEntity {
 
     /** The trust anchors to be used within the API request. */
     @Nonnull @NotEmpty private final Collection<String> trustAnchors;
@@ -43,19 +40,10 @@ public class TrustedRemoteResolverEntity {
      */
     public TrustedRemoteResolverEntity(@Nonnull @NotEmpty @ParameterName(name="entity") final String entity,
             @Nonnull @NotEmpty @ParameterName(name="anchors") final Collection<String> anchors) {
-        entityId = Constraint.isNotEmpty(entity, "Entity ID cannot be empty");
+        super(entity);
         trustAnchors = Constraint.isNotEmpty(anchors, "Trust Anchors cannot be empty");
     }
 
-    /**
-     * Get the entity ID of the remote resolver.
-     * 
-     * @return entity ID of the remote resolver
-     */
-    @Nonnull @NotEmpty public String getEntityId() {
-        return entityId;
-    }
-
     /**
      * Get the trust anchors to be used within the API request.
      * 
@@ -87,7 +75,7 @@ public class TrustedRemoteResolverEntity {
             return false;
         }
         final TrustedRemoteResolverEntity other = (TrustedRemoteResolverEntity) obj;
-        return entityId.equals(other.entityId) &&
+        return getEntityId().equals(other.getEntityId()) &&
                 trustAnchors.containsAll(other.trustAnchors) && other.trustAnchors.containsAll(trustAnchors);
     }
 }
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/TrustMarkRequest.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/TrustMarkRequest.java
new file mode 100644
index 0000000..2a080ab
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/TrustMarkRequest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.messaging.impl;
+
+import java.net.URI;
+
+import javax.annotation.Nonnull;
+
+import com.google.common.base.MoreObjects;
+import com.nimbusds.oauth2.sdk.Request;
+import com.nimbusds.oauth2.sdk.http.HTTPRequest;
+
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * Request message to the OpenID federation Trust Mark endpoint.
+ */
+public class TrustMarkRequest implements Request {
+
+    /** The endpoint URI of the request. */
+    @Nonnull private final URI endpointUri;
+
+    /** The identifier for the type of the Trust Mark. */
+    @Nonnull @NotEmpty private final String trustMarkType;
+
+    /** The entity ID of the Entity to which the Trust Mark is issued. */
+    @Nonnull @NotEmpty private final String subject;
+    
+    /**
+     * Constructor.
+     *
+     * @param uri endpoint URI
+     * @param type trust _mark type
+     * @param sub subject entity ID
+     */
+    public TrustMarkRequest(@Nonnull final URI uri, @Nonnull final String type, @Nonnull final String sub) {
+        endpointUri = Constraint.isNotNull(uri, "Endpoint URI cannot be null");
+        trustMarkType = Constraint.isNotEmpty(type, "Trust Mark type cannot be null or empty");
+        subject = Constraint.isNotEmpty(sub, "Subject cannot be null or empty");
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nonnull
+    public URI getEndpointURI() {
+        return endpointUri;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public HTTPRequest toHTTPRequest() {
+        //TODO
+        return null;
+    }
+
+    /**
+     * Get the trust mark type for the request.
+     * 
+     * @return trust mark type
+     */
+    @Nonnull @NotEmpty public String getTrustMarkType() {
+        return trustMarkType;
+    }
+
+    /**
+     * Get the subject for the request.
+     * 
+     * @return subject
+     */
+    @Nonnull @NotEmpty public String getSubject() {
+        return subject;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("subject", getSubject())
+                .add("trustMarkType", getTrustMarkType())
+                .add("endpointURI", getEndpointURI())
+                .toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final TrustMarkRequest other = (TrustMarkRequest) obj;
+        return endpointUri.equals(other.endpointUri) && subject.equals(other.subject) &&
+                trustMarkType.equals(other.trustMarkType);
+    }
+
+}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/TrustMarkResponse.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/TrustMarkResponse.java
new file mode 100644
index 0000000..c132acb
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/TrustMarkResponse.java
@@ -0,0 +1,87 @@
+/*
+ * 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.messaging.impl;
+
+import javax.annotation.Nonnull;
+
+import com.nimbusds.common.contenttype.ContentType;
+import com.nimbusds.jose.JOSEObjectType;
+import com.nimbusds.jwt.SignedJWT;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.http.HTTPResponse;
+
+import net.shibboleth.shared.logic.ConstraintViolationException;
+import net.shibboleth.shared.primitive.StringSupport;
+
+/**
+ * Response message to the OpenID federation Trust Mark endpoint.
+ */
+public class TrustMarkResponse extends AbstractSignedJWTResponse {
+
+    /** The JWT type header. */
+    @Nonnull
+    public static final JOSEObjectType JWT_TYPE_HEADER = new JOSEObjectType("trust-mark+jwt");
+
+    /** The content type. */
+    @Nonnull public static final ContentType HTTP_RESPONSE_CONTENT_TYPE =
+            new ContentType("application", JWT_TYPE_HEADER.toString());
+
+    /**
+     * Constructor.
+     *
+     * @param statement JWT
+     */
+    public TrustMarkResponse(@Nonnull final SignedJWT statement) {
+        super(statement);
+    }
+
+    /** {@inheritDoc} */
+    protected ContentType getHttpResponseContentType() {
+        return HTTP_RESPONSE_CONTENT_TYPE;
+    }
+
+    /** {@inheritDoc} */
+    protected JOSEObjectType getJWTTypeHeader() {
+        return JWT_TYPE_HEADER;
+    }
+
+    /**
+     * Parses a trust mark success response from the given HTTP response.
+     *
+     * @param httpResponse the HTTP response
+     * @return resolve entity success response
+     * @throws ParseException if HTTP response could not be parsed into trust mark response
+     */
+    @Nonnull
+    public static TrustMarkResponse parse(@Nonnull final HTTPResponse httpResponse)
+        throws ParseException {
+
+        httpResponse.ensureStatusCode(HTTPResponse.SC_OK);
+        httpResponse.ensureEntityContentType(HTTP_RESPONSE_CONTENT_TYPE);
+        final String content = httpResponse.getContent();
+
+        if (StringSupport.trimOrNull(content) == null) {
+            throw new ParseException("Message body is empty");
+        }
+
+        try {
+            final SignedJWT jwt = SignedJWT.parse(httpResponse.getContent());
+            assert jwt != null;
+            return new TrustMarkResponse(jwt);
+        } catch (final java.text.ParseException | ConstraintViolationException e) {
+            throw new ParseException(e.getMessage(), e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkFetchingStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkFetchingStrategy.java
new file mode 100644
index 0000000..890d024
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkFetchingStrategy.java
@@ -0,0 +1,114 @@
+/*
+ * 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.metadata;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.apache.hc.core5.net.URIBuilder;
+import org.slf4j.Logger;
+
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.http.HTTPResponse;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkRequest;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkResponse;
+import net.shibboleth.shared.annotation.constraint.ThreadSafeAfterInit;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.resolver.CriteriaSet;
+
+/**
+ * Default strategy for fetching trust mark via Trust Mark API for the request specified in the criteria set. The
+ * response is stored inside a {@link TrustMarkResponseContainer}.
+ */
+ at ThreadSafeAfterInit
+public class DefaultTrustMarkFetchingStrategy
+    extends AbstractFederationEndpointResponseFetchingStrategy<TrustMarkRequest, TrustMarkResponseContainer> {
+
+    /** Class logger. */
+    @Nonnull private Logger log = LoggerFactory.getLogger(DefaultTrustMarkFetchingStrategy.class);
+
+    /**
+     * Constructor.
+     */
+    public DefaultTrustMarkFetchingStrategy() {
+        setCriteriaToRequestStrategy(criteria -> {
+            final TrustMarkRequestCriterion requestCriterion = criteria.get(TrustMarkRequestCriterion.class);
+            if (requestCriterion == null) {
+                log.debug("No request criterion given, returning null");
+                return null;
+            }
+            return requestCriterion.getRequest();
+        });
+    }
+
+    /** {@inheritDoc} */
+    @Nullable protected ClassicHttpRequest initializeHttpRequest(@Nonnull final CriteriaSet criteria,
+            @Nonnull TrustMarkRequest request) {
+        final HttpGet httpRequest = new HttpGet(request.getEndpointURI());
+        final List<NameValuePair> nvps = new ArrayList<>();
+        nvps.add(new BasicNameValuePair("trust_mark_type", request.getTrustMarkType()));
+        nvps.add(new BasicNameValuePair("sub", request.getSubject()));
+        try {
+            final URI uri = new URIBuilder(httpRequest.getUri()).addParameters(nvps).build();
+            httpRequest.setUri(uri);
+        } catch (final URISyntaxException e) {
+            log.error("Could not create URI with the given parameters {}", request, e);
+        }
+        return httpRequest;
+    }
+
+    /** {@inheritDoc} */
+    @Nullable protected TrustMarkResponseContainer parseHttpResponse(@Nonnull final CriteriaSet criteria,
+            @Nonnull final TrustMarkRequest request, @Nullable final ClassicHttpResponse response)
+                    throws ProtocolException, IOException {
+        final ResponseContainerExpirationCriterion expirationCriterion =
+                criteria.get(ResponseContainerExpirationCriterion.class);
+        if (expirationCriterion == null) {
+            log.debug("No expiration criterion given, returning null");
+            return null;
+        }
+        if (response != null) {
+            final HTTPResponse nimbusResponse = new HTTPResponse(response.getCode());
+            nimbusResponse.setContent(EntityUtils.toString(response.getEntity()));
+            try {
+                nimbusResponse.setContentType(response.getEntity().getContentType());
+                final TrustMarkResponse parsedResponse = TrustMarkResponse.parse(nimbusResponse);
+                final Instant expirationTime = expirationCriterion.getExpirationInstant();
+                return new TrustMarkResponseContainer(parsedResponse, request, parsedResponse.getJWT(), expirationTime);
+            } catch (final ParseException e) {
+                log.warn("Could not parse resolve entity response from URI: {}", request.getEndpointURI(), e);
+            }
+        } else {
+            log.debug("Unable to fetch resolve entity response from URI: {} (null response)", request.getEndpointURI());
+        }
+
+        return null;
+    }
+}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkResponseContainerExpirationTimeStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkResponseContainerExpirationTimeStrategy.java
new file mode 100644
index 0000000..a76484c
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkResponseContainerExpirationTimeStrategy.java
@@ -0,0 +1,59 @@
+/*
+ * 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.metadata;
+
+import java.text.ParseException;
+import java.time.Instant;
+import java.util.function.Function;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkResponse;
+import net.shibboleth.oidc.metadata.cache.ExpirationTimeContext;
+
+/**
+ * Default strategy for fetching expiration time for the trust mark response container. The expiration instant is
+ * fetched from which is before: the success response message's JWT expiration time or the instant returned by
+ * {@link TrustMarkResponseContainer#getExpirationInstant()}.
+ */
+ at ThreadSafe
+public class DefaultTrustMarkResponseContainerExpirationTimeStrategy
+        implements Function<ExpirationTimeContext<TrustMarkResponseContainer>, Instant> {
+
+    /** {@inheritDoc} */
+    @Nullable public Instant apply(@Nullable final ExpirationTimeContext<TrustMarkResponseContainer> context) {
+        if (context == null) {
+            return null;
+        }
+        final Instant contextExpiration = context.getNow().plus(context.getMaxCacheDuration());
+        final TrustMarkResponseContainer container = context.getMetadata();
+        if (container == null || container.getExpirationInstant() == null) {
+            return contextExpiration;
+        }
+        final Instant containerExpiration = container.getExpirationInstant();
+        if (container.getResponse() instanceof TrustMarkResponse successResponse) {
+            try {
+                final Instant jwtExpiration =
+                        successResponse.getJWT().getJWTClaimsSet().getExpirationTime().toInstant();
+                return jwtExpiration.isBefore(containerExpiration) ? jwtExpiration : containerExpiration;
+            } catch (final ParseException e) {
+                // ignore, use container expiration
+            }
+        }
+        return containerExpiration;
+    }
+
+}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkResponseSignatureValidationFilterStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkResponseSignatureValidationFilterStrategy.java
new file mode 100644
index 0000000..4cebae4
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustMarkResponseSignatureValidationFilterStrategy.java
@@ -0,0 +1,128 @@
+/*
+ * 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.metadata;
+
+import java.text.ParseException;
+import java.util.List;
+import java.util.function.BiFunction;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.security.trust.TrustEngine;
+import org.slf4j.Logger;
+
+import com.nimbusds.jwt.SignedJWT;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkResponse;
+import net.shibboleth.oidc.metadata.cache.MetadataCache;
+import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
+import net.shibboleth.oidc.metadata.filter.MetadataFilterContext;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.ThreadSafeAfterInit;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.resolver.CriteriaSet;
+
+/**
+ * Default signature validating filter for Trust Mark response. The signature validation is performed via
+ * configurable {@link TrustEngine}.
+ */
+ at ThreadSafeAfterInit
+public class DefaultTrustMarkResponseSignatureValidationFilterStrategy
+    extends AbstractTrustEngineSignatureValidationComponent
+    implements BiFunction<TrustMarkResponseContainer, MetadataFilterContext, TrustMarkResponseContainer> {
+
+    /** Class logger. */
+    @Nonnull private Logger log =
+            LoggerFactory.getLogger(DefaultTrustMarkResponseSignatureValidationFilterStrategy.class);
+
+    /** Cache used to fetch the issuer entity configuration from. */
+    @NonnullAfterInit private MetadataCache<List<List<EntityStatement>>> trustChainCache;
+
+    /**
+     * Set the cache used to fetch the trust chain for the trust mark issuer from.
+     * 
+     * @param cache cache used to fetch the trust chain for the trust mark issuer from
+     */
+    public void setTrustChainCache(@Nonnull final MetadataCache<List<List<EntityStatement>>> cache) {
+        checkSetterPreconditions();
+        trustChainCache = Constraint.isNotNull(cache, "Trust Chain cache cannot be null");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        if (trustChainCache == null) {
+            throw new ComponentInitializationException("Trust Chain cache cannot be null");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable
+    public TrustMarkResponseContainer apply(@Nullable final TrustMarkResponseContainer response,
+            @Nullable final MetadataFilterContext filterContext) {
+        checkComponentActive();
+        if (response == null) {
+            return null;
+        }
+        if (response.getResponse() instanceof TrustMarkResponse successResponse) {
+            final SignedJWT jwt = successResponse.getJWT();
+            final String entityId;
+            try {
+                entityId = jwt.getJWTClaimsSet().getIssuer();
+            } catch (final ParseException e) {
+                log.error("Could not parse issuer entity ID value from the response", e);
+                return null;
+            }
+            final EntityStatement issuerStatement = fetchIssuerStatement(entityId);
+            if (issuerStatement == null) {
+                return null;
+            }
+            log.trace("Starting signature validation of success response from {}", entityId);
+            final CriteriaSet criteria = new CriteriaSet(new SubjectEntityStatementCriterion(issuerStatement));
+            if (validateJwt(jwt, criteria, entityId)) {
+                return response;
+            }
+        } else {
+            log.trace("Ignoring signature validation for the error response");
+            return response;
+        }
+        return null;
+    }
+
+    /**
+     * Fetch the issuer entity configuration from the trust chain metadata cache.
+     * 
+     * @param issuer the issuer entity ID
+     * @return the issuer entity configuration, or null if could not be fetched
+     */
+    @Nullable protected EntityStatement fetchIssuerStatement(@Nonnull final String issuer) {
+        final CriteriaSet criteria = new CriteriaSet(new SubjectEntityIDCriterion(issuer));
+        try {
+            final List<List<List<EntityStatement>>> result = trustChainCache.get(criteria);
+            if (!result.isEmpty() && !result.get(0).isEmpty() && !result.get(0).get(0).isEmpty()) {
+                return result.get(0).get(0).get(0);
+            }
+        } catch (final MetadataCacheException e) {
+            log.debug("Error while fetching issuer trust chain for {}", issuer, e);
+        }
+        log.warn("Could not fetch entity configuration for {}", issuer);
+        return null;
+    }
+}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/TrustMarkRequestCriterion.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/TrustMarkRequestCriterion.java
new file mode 100644
index 0000000..2629074
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/TrustMarkRequestCriterion.java
@@ -0,0 +1,79 @@
+/*
+ * 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.metadata;
+
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkRequest;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.resolver.Criterion;
+
+/**
+ * A {@link Criterion} representing request message to a Trust Mark API.
+ */
+public class TrustMarkRequestCriterion implements Criterion {
+
+    /** The request message. */
+    @Nonnull private final TrustMarkRequest request;
+
+    /**
+     * Constructor.
+     *
+     * @param requestMessage request message, must not be null
+     */
+    public TrustMarkRequestCriterion(@Nonnull final TrustMarkRequest requestMessage) {
+        request = Constraint.isNotNull(requestMessage, "Request cannot be null");
+    }
+
+    /**
+     * Get the request message.
+     * 
+     * @return the request message
+     */
+    @Nonnull public TrustMarkRequest getRequest() {
+        return request;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "TrustMarkRequestCriterion [request=" + request + "]";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return Objects.hash(request);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final TrustMarkRequestCriterion other = (TrustMarkRequestCriterion) obj;
+        return request.equals(other.request);
+    }
+
+}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/TrustMarkResponseContainer.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/TrustMarkResponseContainer.java
new file mode 100644
index 0000000..755b8fc
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/TrustMarkResponseContainer.java
@@ -0,0 +1,75 @@
+/*
+ * 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.metadata;
+
+import java.io.Serializable;
+import java.time.Instant;
+
+import javax.annotation.Nonnull;
+
+import com.nimbusds.jwt.SignedJWT;
+import com.nimbusds.oauth2.sdk.Response;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkRequest;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * A container class for metadata caches carrying request and response message details related to Trust Mark API.
+ */
+public class TrustMarkResponseContainer extends NimbusResponseContainer implements Serializable {
+
+    /** Serial version UID. */
+    private static final long serialVersionUID = 756269383556865370L;
+
+    /** Request message used for obtaining the Trust Mark. */
+    @Nonnull private final TrustMarkRequest request;
+
+    /** Trust Mark. */
+    @Nonnull private final SignedJWT trustMark;
+
+    /**
+     * Constructor.
+     *
+     * @param responseMessage response message
+     * @param requestMessage request message
+     * @param jwt trust mark
+     * @param expirationInstant expiration instant
+     */
+    public TrustMarkResponseContainer(@Nonnull final Response responseMessage,
+            @Nonnull final TrustMarkRequest requestMessage, @Nonnull final SignedJWT jwt,
+            @Nonnull final Instant expirationInstant) {
+        super(responseMessage, expirationInstant);
+        request = Constraint.isNotNull(requestMessage, "Request nessage cannot be null");
+        trustMark = Constraint.isNotNull(jwt, "Trust Mark JWT cannot be null");
+    }
+
+    /**
+     * Get request message.
+     * 
+     * @return request message
+     */
+    public TrustMarkRequest getRequestMessage() {
+        return request;
+    }
+    
+    /**
+     * Get trust mark.
+     * 
+     * @return trust mark
+     */
+    public SignedJWT getTrustMark() {
+        return trustMark;
+    }
+}
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 f453ba7..be1f5b4 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
@@ -74,6 +74,9 @@ public class BuildEntityConfiguration extends AbstractBuildEntityStatementAction
     /** Strategy used to locate trust_anchor_hints. */
     @NonnullAfterInit private Function<ProfileRequestContext,List<String>> trustAnchorHintsLookupStrategy;
 
+    /** Strategies used to locate values for optional claims. */
+    @NonnullAfterInit private Map<String, Function<ProfileRequestContext,Object>> optionalClaimsLookupStrategies;
+
     /** Metadata to publish. */
     @NonnullBeforeExec private Map<String,Map<String,Object>> metadata;
 
@@ -134,6 +137,19 @@ public class BuildEntityConfiguration extends AbstractBuildEntityStatementAction
                 Constraint.isNotNull(strategy, "Trust anchor hints lookup strategy cannot be null");
     }
 
+    /**
+     * Set the map of lookup strategies to locate values for optional claims.
+     * 
+     * @param strategies map of lookup strategies
+     */
+    public void setOptionalClaimsLookupStrategies(
+            @Nonnull final Map<String,Function<ProfileRequestContext,Object>> strategies) {
+        checkSetterPreconditions();
+
+        optionalClaimsLookupStrategies =
+                Constraint.isNotNull(strategies, "Optional claims lookup strategies cannot be null");
+    }
+
     /** {@inheritDoc} */
     @Override
     protected void doInitialize() throws ComponentInitializationException {
@@ -142,6 +158,9 @@ public class BuildEntityConfiguration extends AbstractBuildEntityStatementAction
         if (trustAnchorHintsLookupStrategy == null) {
             throw new ComponentInitializationException("Trust anchor hints lookup strategy cannot be null");
         }
+        if (optionalClaimsLookupStrategies == null) {
+            throw new ComponentInitializationException("Optional claims lookup strategies cannot be null");
+        }
     }
 
     /** {@inheritDoc} */
@@ -205,7 +224,19 @@ public class BuildEntityConfiguration extends AbstractBuildEntityStatementAction
         if (trustAnchorHints != null && !trustAnchorHints.isEmpty()) {
             builder.claim("trust_anchor_hints", trustAnchorHints);
         }
-        return true;
+        for (final String claim : optionalClaimsLookupStrategies.keySet()) {
+            log.trace("{} Looking up the value for clain {}", getLogPrefix(), claim);
+            final Function<ProfileRequestContext,Object> lookup = optionalClaimsLookupStrategies.get(claim);
+            final Object value = lookup.apply(profileRequestContext);
+            if (value != null) {
+                log.debug("{} Resolved value {} for clain {}", getLogPrefix(), value, claim);
+                builder.claim(claim, value);
+            } else {
+                log.debug("{} No value resolved for clain {}", getLogPrefix(), claim);
+            }
+        }
+
+       return true;
    }
 
 }
\ 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/DefaultTrustMarkFromMetadataCacheFetchingFunction.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/DefaultTrustMarkFromMetadataCacheFetchingFunction.java
new file mode 100644
index 0000000..90bd729
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/DefaultTrustMarkFromMetadataCacheFetchingFunction.java
@@ -0,0 +1,204 @@
+/*
+ * 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.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;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkRequest;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkResponse;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatementHelper;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.ResponseContainerExpirationCriterion;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.TrustMarkRequestCriterion;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.TrustMarkResponseContainer;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.TrustedRemoteEntity;
+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.NotEmpty;
+import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.resolver.CriteriaSet;
+
+/**
+ * Function to fetch a trust mark from the configured cache of trust marks. The configurable cache is used for
+ * fetching the trust_mark_endpoint of the trusted entity.
+ */
+public class DefaultTrustMarkFromMetadataCacheFetchingFunction extends AbstractIdentifiableInitializableComponent
+    implements Function<ProfileRequestContext, Map<String, String>> {
+
+    /** Class logger. */
+    @Nonnull private Logger log = LoggerFactory.getLogger(DefaultTrustMarkFromMetadataCacheFetchingFunction.class);
+
+    /** Cache used to fetch the issuer entity configuration from. */
+    @NonnullAfterInit private MetadataCache<EntityStatement> entityConfigurationCache;
+
+    /** Cache containing responses from Trust Mark APIs. */
+    @NonnullAfterInit private MetadataCache<TrustMarkResponseContainer> trustMarkCache;
+
+    /** JSON object mapper used for decoding JSON into Map. */
+    @NonnullAfterInit private ObjectMapper objectMapper;
+
+    /** Trusted trust mark API entity. */
+    @NonnullAfterInit private TrustedRemoteEntity trustedEntity;
+
+    /** Trust mark type. */
+    @NonnullAfterInit private String trustMarkType;
+
+    /** 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.
+     * 
+     * @param cache cache used to fetch the issuer entity configuration from
+     */
+    public void setEntityConfigurationCache(@Nonnull final MetadataCache<EntityStatement> cache) {
+        checkSetterPreconditions();
+        entityConfigurationCache = Constraint.isNotNull(cache, "Entity Configuration cache cannot be null");
+    }
+
+    /**
+     * Set the cache containing responses from Trust Mark APIs.
+     * 
+     * @param cache cache containing responses from Trust Mark APIs
+     */
+    public void setTrustMarkCache(@Nonnull final MetadataCache<TrustMarkResponseContainer> cache) {
+        checkSetterPreconditions();
+        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.
+     * 
+     * @param entity trusted trust mark API entity
+     */
+    public void setTrustedEntity(@Nonnull final TrustedRemoteEntity entity) {
+        checkSetterPreconditions();
+        trustedEntity = Constraint.isNotNull(entity, "Trusted entity cannot be null");
+    }
+
+    /**
+     * Set the trust mark type.
+     * 
+     * @param type trust mark type
+     */
+    public void setTrustMarkType(@Nonnull @NotEmpty final String type) {
+        checkSetterPreconditions();
+        trustMarkType = Constraint.isNotEmpty(type, "Trust mark type cannot be empty");
+    }
+
+    /**
+     * Set the subject.
+     * 
+     * @param sub subject
+     */
+    public void setSubject(@Nonnull @NotEmpty final String sub) {
+        checkSetterPreconditions();
+        subject = Constraint.isNotEmpty(sub, "Subject cannot be empty");
+    }
+
+    /** {@inheritDoc} */
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        if (entityConfigurationCache == null) {
+            throw new ComponentInitializationException("Entity configuration cache cannot be null");
+        }
+        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");
+        }
+        if (trustMarkType == null) {
+            throw new ComponentInitializationException("Trust mark type cannot be null");
+        }
+        if (subject == null) {
+            throw new ComponentInitializationException("Subject cannot be null");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable
+    public Map<String, String> apply(@Nullable final ProfileRequestContext profileRequestContext) {
+        checkComponentActive();
+        final String entityId = trustedEntity.getEntityId();
+        final URI uri = EntityStatementHelper.fetchEndpointUriFromFederationEntity(entityConfigurationCache,
+                entityId, "trust_mark_endpoint", objectMapper);
+        if (uri == null) {
+            log.warn("Could not fetch trust mark endpoint for {}", entityId);
+            return null;
+        }
+        final TrustMarkRequest trustMarkRequest = new TrustMarkRequest(uri, trustMarkType, subject);
+        final CriteriaSet criteriaSet = new CriteriaSet(new TrustMarkRequestCriterion(trustMarkRequest));
+        criteriaSet.add(new ResponseContainerExpirationCriterion(Instant.now().plus(cachedLifetime)));
+        final List<TrustMarkResponseContainer> cacheResult;
+        try {
+            cacheResult = trustMarkCache.get(criteriaSet);
+        } catch (final MetadataCacheException e) {
+            log.warn("Could not resolve trust mark {} from {}", trustMarkType, trustedEntity, e);
+            return null;
+        }
+        if (cacheResult.isEmpty()) {
+            log.debug("No data resolved for {} from {}", trustMarkType, trustedEntity);
+            return null;
+        }
+        if (cacheResult.get(0).getResponse() instanceof TrustMarkResponse successResponse) {
+            return Map.of("trust_mark_type", trustMarkType, "trust_mark", successResponse.getJWT().serialize());
+        } else {
+            log.debug("The response from {} was not a success response: {}", trustedEntity,
+                    cacheResult.get(0).getResponse());
+        }
+        return null;
+    }
+}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultEntityConfigurationTrustMarksLookupStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultEntityConfigurationTrustMarksLookupStrategy.java
new file mode 100644
index 0000000..fd2f506
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultEntityConfigurationTrustMarksLookupStrategy.java
@@ -0,0 +1,77 @@
+/*
+ * 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.navigate;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * Default strategy to fetch trust marks to be included in the entity configuration.
+ */
+public class DefaultEntityConfigurationTrustMarksLookupStrategy extends AbstractIdentifiableInitializableComponent
+    implements Function<ProfileRequestContext,List<Map<String,String>>> {
+
+    /** Lookup strategies to fetch trust mark values to be included in the entity configuration. */
+    @Nonnull private List<Function<ProfileRequestContext,Map<String,String>>> trustMarkLookupStrategies;
+
+    /**
+     * Constructor.
+     */
+    public DefaultEntityConfigurationTrustMarksLookupStrategy() {
+        trustMarkLookupStrategies = CollectionSupport.emptyList();
+    }
+
+    /**
+     * Set the lookup strategies to fetch trust mark values to be included in the entity configuration.
+     * 
+     * @param strategies lookup strategies
+     */
+    public void setTrustMarkLookupStrategies(
+            @Nonnull final List<Function<ProfileRequestContext,Map<String,String>>> strategies) {
+        checkSetterPreconditions();
+        Constraint.isNotNull(strategies, "Trust mark lookup strategies cannot be null");
+        trustMarkLookupStrategies = strategies;
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nonnull
+    public List<Map<String, String>> apply(@Nullable final ProfileRequestContext profileRequestContext) {
+        checkComponentActive();
+        final List<Map<String, String>> trustMarks = new ArrayList<>();
+        for (final Function<ProfileRequestContext,Map<String,String>> strategy : trustMarkLookupStrategies) {
+            if (strategy == null) {
+                continue;
+            }
+            Optional.ofNullable(strategy.apply(profileRequestContext))
+                .filter(trustMark -> trustMark != null && !trustMark.isEmpty())
+                .ifPresent(trustMark -> trustMarks.add(trustMark));
+            
+        }
+        return CollectionSupport.copyToList(trustMarks);
+    }
+
+}
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 ea4b2d8..7230ea8 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
@@ -328,6 +328,39 @@
         </property>
     </bean>
 
+    <bean id="shibboleth.oidfed.TrustMarkMetadataCache" parent="shibboleth.oidc.CacheBuilder">
+        <constructor-arg>
+            <bean p:cacheId="DefaultTrustMarkMetadataCache" parent="shibboleth.oidfed.TrustMarkMetadataCacheBuilderSpec"
+                p:cleanupTaskInterval="PT30S"/>
+        </constructor-arg>
+    </bean>
+
+    <bean id="shibboleth.oidfed.TrustMarkMetadataCacheBuilderSpec"
+        class="net.shibboleth.oidc.metadata.cache.impl.DynamicMetadataCacheBuilderSpec"
+        p:minCacheDuration="%{idp.oidfed.cache.trustMark.minRefreshDelay:PT1H}"
+        p:maxCacheDuration="%{idp.oidfed.cache.trustMark.maxRefreshDelay:PT24H}">
+        <property name="identifierExtractionStrategy">
+            <bean parent="shibboleth.Functions.Expression" c:expression="#input?.getRequestMessage().toString()"/>
+        </property>
+        <property name="criteriaToIdentifierStrategy">
+            <bean parent="shibboleth.Functions.Expression" c:expression="#input?.get(T(net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.TrustMarkRequestCriterion))?.getRequest().toString()"/>
+        </property>
+        <property name="metadataFilterStrategy">
+            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultTrustMarkResponseSignatureValidationFilterStrategy"
+                p:trustChainCache-ref="shibboleth.oidfed.TrustChainMetadataCache"
+                p:trustEngine-ref="shibboleth.oidfed.DefaultEntityConfigurationTrustEngine"/>
+        </property>
+        <property name="fetchStrategy">
+            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultTrustMarkFetchingStrategy"
+                p:httpClient="#{getObject('shibboleth.oidfed.HttpClient') ?: getObject('shibboleth.InternalHttpClient')}"
+                p:httpClientSecurityParameters="#{getObject('shibboleth.oidfed.NonBrowser.HttpClientSecurityParameters')}"
+                p:objectMapper-ref="shibboleth.oidfed.policy.JSONObjectMapper"/>
+        </property>
+        <property name="metadataExpirationTimeStrategy">
+            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultTrustMarkResponseContainerExpirationTimeStrategy"/>
+        </property>
+    </bean>
+
     <bean id="shibboleth.oidfed.DefaultDelegatedTrustMarkTrustEngine"
         class="net.shibboleth.oidc.security.impl.ExplicitKeySignedJWTTrustEngine">
         <constructor-arg index="0">
@@ -672,6 +705,14 @@
     <alias alias="UseResolverApiCondition" name="%{idp.oidfed.trustchain.resolver.useResolverApiCondition:shibboleth.Conditions.FALSE}" />
     <alias alias="FallbackToLocalResolutionCondition" name="%{idp.oidfed.trustchain.resolver.fallbackToLocalCondition:shibboleth.Conditions.TRUE}" />
 
+    <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"/>
+
     <import resource="${idp.home}/conf/oidfed/oidfed-trustchain-resolver.xml"/>
+    <import resource="${idp.home}/conf/oidfed/oidfed-entity-configuration-claims.xml"/>
 
 </beans>
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/entity-configuration/entity-configuration-beans.xml b/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/entity-configuration/entity-configuration-beans.xml
index 9eb731d..053f4fe 100644
--- a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/entity-configuration/entity-configuration-beans.xml
+++ b/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/entity-configuration/entity-configuration-beans.xml
@@ -98,7 +98,8 @@
         class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.BuildEntityConfiguration" scope="prototype"
         p:identifierGeneratorLookupStrategy-ref="shibboleth.oidc.DefaultIdentifierGenerationStrategy"
         p:objectMapper-ref="#{'%{idp.oidc.logging.objectMapper:shibboleth.oidc.JSONObjectMapper}'.trim()}"
-        p:trustAnchorHintsLookupStrategy-ref="#{'%{idp.oidfed.entity-configuration.trustAnchoHintsLookup:DefaultTrustAnchorHintsLookupStrategy}'.trim()}"/>
+        p:trustAnchorHintsLookupStrategy-ref="#{'%{idp.oidfed.entity-configuration.trustAnchoHintsLookup:DefaultTrustAnchorHintsLookupStrategy}'.trim()}"
+        p:optionalClaimsLookupStrategies-ref="shibboleth.oidfed.EntityConfigurationClaimLookupStrategies" />
 
     <bean id="DefaultTrustAnchorHintsLookupStrategy"
         class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultTrustAnchorHintsLookupStrategy"
diff --git a/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-entity-configuration-claims.xml b/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-entity-configuration-claims.xml
new file mode 100644
index 0000000..dac0420
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-entity-configuration-claims.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:context="http://www.springframework.org/schema/context"
+    xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
+    xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+    default-init-method="initialize" default-destroy-method="destroy">
+
+    <!-- TODO: document with examples once the structure is settled -->
+    <util:map id="shibboleth.oidfed.EntityConfigurationClaimLookupStrategies"
+        value-type="java.util.function.Function">
+    </util:map>
+
+</beans>
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/module.properties b/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/module.properties
index 54d11a0..854bdda 100644
--- a/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/module.properties
+++ b/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/module.properties
@@ -19,3 +19,7 @@ idp.oidc.OP.oidfed.2.replace = false
 idp.oidc.OP.oidfed.3.src =  /net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-trustchain-resolver.xml
 idp.oidc.OP.oidfed.3.dest = conf/oidfed/oidfed-trustchain-resolver.xml
 idp.oidc.OP.oidfed.3.replace = false
+
+idp.oidc.OP.oidfed.4.src =  /net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-entity-configuration-claims.xml
+idp.oidc.OP.oidfed.4.dest = conf/oidfed/oidfed-entity-configuration-claims.xml
+idp.oidc.OP.oidfed.4.replace = false
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java
index 75555b4..d2526e8 100644
--- a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java
@@ -80,7 +80,6 @@ import net.shibboleth.idp.plugin.oidc.op.oidfed.TrustChainTestUtil;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.ResolveEntityResponse;
 import net.shibboleth.idp.plugin.oidc.op.profile.flow.AbstractOidcFlowTest;
 import net.shibboleth.oidc.security.credential.BasicJWKCredential;
-import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.logic.Constraint;
 
 /**
@@ -100,6 +99,7 @@ public class AbstractFederationFlowTest extends AbstractOidcFlowTest {
     final String anchorFetchEndpoint = anchorId + "/fetch";
     final String anchorResolveEndpoint = anchorId + "/resolve";
     final String trustMarkIssuerId = "https://trust-mark-issuer.federation.local";
+    final String trustMarkEndpoint = "https://trust-mark-issuer.federation.local/issue";
     String issuer = "https://op.example.org";
 
     JWK rpKey;
@@ -282,7 +282,8 @@ public class AbstractFederationFlowTest extends AbstractOidcFlowTest {
                 .claim("jwks", new JWKSet(trustMarkIssuerKey).toJSONObject(true))
                 .claim("authority_hints", authorityHints == null || authorityHints.length == 0 ?
                         new String[] { anchorId } : authorityHints)
-                .claim("metadata", Map.of("federation_entity", CollectionSupport.emptyMap()))
+                .claim("metadata", Map.of("federation_entity", Map.of("trust_mark_endpoint",
+                        trustMarkEndpoint)))
                 .build();
         final EntityStatement rpConfiguration =
                 TrustChainTestUtil.entityStatement(JWSAlgorithm.RS256, trustMarkIssuerKey, claimsSet);
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/EntityConfigurationFlowTest.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/EntityConfigurationFlowTest.java
index 12d6846..25729f6 100644
--- a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/EntityConfigurationFlowTest.java
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/EntityConfigurationFlowTest.java
@@ -17,16 +17,21 @@ package net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed;
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 
 import org.springframework.webflow.executor.FlowExecutionResult;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.nimbusds.jose.Algorithm;
+import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.oauth2.sdk.ParseException;
 import com.nimbusds.oauth2.sdk.Response;
 import com.nimbusds.oauth2.sdk.id.Issuer;
@@ -36,6 +41,10 @@ import com.nimbusds.openid.connect.sdk.federation.entities.FederationEntityMetad
 import com.nimbusds.openid.connect.sdk.federation.registration.ClientRegistrationType;
 import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
 
+import net.shibboleth.idp.plugin.oidc.op.oidfed.TrustChainTestUtil;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.TrustMarkResponse;
+import net.shibboleth.shared.collection.CollectionSupport;
+
 /**
  * Unit test for the entity configuration flow.
  */
@@ -43,12 +52,34 @@ public class EntityConfigurationFlowTest extends AbstractFederationFlowTest {
 
     public static final String FLOW_ID = "oidfed/entity-configuration";
 
+    final String dynamicTrustMarkIssuerId = "https://dyn-trust-mark-issuer.federation.local";
+    final String dynamicTrustMarkType = dynamicTrustMarkIssuerId + "/example";
+    
     protected EntityConfigurationFlowTest() {
         super(FLOW_ID);
     }
 
     @Test
     public void testOutputAndCaching() throws ParseException, IOException, InterruptedException {
+        final String trustMark = TrustChainTestUtil.trustMark(JWSAlgorithm.RS256, trustMarkIssuerKey,
+                dynamicTrustMarkIssuerId, issuer, dynamicTrustMarkType, Instant.now().plusSeconds(300)).serialize();
+        try {
+            mapResponse(entityConfigurationUrl(dynamicTrustMarkIssuerId),
+                    mockResponse(trustMarkIssuerConfiguration(dynamicTrustMarkIssuerId)));
+
+            mapResponse(entityConfigurationUrl(anchorId), mockResponse(trustedAnchorConfiguration()));
+            mapResponse(subordinateStatementUrl(anchorFetchEndpoint, dynamicTrustMarkIssuerId),
+                    mockResponse(subordinateStatement(dynamicTrustMarkIssuerId,
+                            Map.of("federation_entity", CollectionSupport.emptyMap()), trustMarkIssuerKey)));
+            final String trustMarkUrl = trustMarkEndpoint + "?trust_mark_type=" 
+                    + URLEncoder.encode(dynamicTrustMarkType, Charset.forName("UTF-8")) 
+                    + "&sub=" + URLEncoder.encode(issuer, Charset.forName("UTF-8"));
+            mapResponse(trustMarkUrl,
+                    mockResponse(200, TrustMarkResponse.HTTP_RESPONSE_CONTENT_TYPE.toString(), trustMark));
+        } catch (UnsupportedOperationException | IOException e) {
+            Assert.fail("Could not initialize mock HTTP client", e);
+        }
+
         request.setRequestURI("/idp/profile/oidfed/entity-configuration");
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         final Response response = parseResponse(result);
@@ -108,6 +139,24 @@ public class EntityConfigurationFlowTest extends AbstractFederationFlowTest {
                 List.of(new EntityID("https://anchor1.example.org"), new EntityID("https://anchor2.example.org")));
         final List<String> trustAnchorHints = entityStatement.getClaimsSet().getStringListClaim("trust_anchor_hints");
         Assert.assertEquals(trustAnchorHints, List.of(anchorId));
+        final List<Object> trustMarks = entityStatement.getClaimsSet().getJSONArrayClaim("trust_marks");
+        Assert.assertNotNull(trustMarks);
+        @SuppressWarnings("unchecked")
+        final List<Map<String,Object>> trustMarkMap = trustMarks.stream().filter(Map.class::isInstance)
+                .map(object -> (Map<String,Object>) object).toList();
+        Assert.assertEquals(trustMarkMap.size(), 2);
+        Assert.assertNotNull(trustMarkMap.stream()
+                .filter(map -> map.entrySet().stream()
+                        .filter(entry -> "trust_mark_type".equals(entry.getKey()) &&
+                                "https://example.org/a-trust-mark".equals(entry.getValue()))
+                        .findAny().isPresent())
+                .findAny().orElse(null));
+        Assert.assertNotNull(trustMarkMap.stream()
+                .filter(map -> map.entrySet().stream()
+                        .filter(entry -> "trust_mark_type".equals(entry.getKey()) &&
+                                dynamicTrustMarkType.equals(entry.getValue()))
+                        .findAny().isPresent())
+                .findAny().orElse(null));
     }
 
     protected boolean containsAll(Collection<? extends Algorithm> algs, Collection<String> strings) {
diff --git a/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed-entity-configuration-claims.xml b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed-entity-configuration-claims.xml
new file mode 100644
index 0000000..26504a9
--- /dev/null
+++ b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed-entity-configuration-claims.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:context="http://www.springframework.org/schema/context"
+    xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
+    xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+    default-init-method="initialize" default-destroy-method="destroy">
+
+    <bean id="ExampleTrustedTrustMarkIssuer"
+        class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.TrustedRemoteEntity"
+        c:entity="https://dyn-trust-mark-issuer.federation.local"/>
+
+    <util:map id="shibboleth.oidfed.EntityConfigurationClaimLookupStrategies"
+        value-type="java.util.function.Function">
+        <entry key="trust_marks">
+            <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultEntityConfigurationTrustMarksLookupStrategy">
+                <property name="trustMarkLookupStrategies">
+                    <util:list value-type="java.util.function.Function">
+                        <bean parent="shibboleth.oidfed.RemoteTrustMark"
+                            p:trustMarkType="https://dyn-trust-mark-issuer.federation.local/example"
+                            p:trustedEntity-ref="ExampleTrustedTrustMarkIssuer" />
+                        <bean parent="shibboleth.Functions.Constant">
+                            <constructor-arg name="target">
+                                <util:map key-type="java.lang.String" value-type="java.lang.String">
+                                    <entry
+                                        key="trust_mark_type"
+                                        value="https://example.org/a-trust-mark" />
+                                    <entry
+                                        key="trust_mark"
+                                        value="eyJraWQiOiJtb2NrVHJ1c3RNYXJrSXNzdWVyS2V5IiwidHlwIjoidHJ1c3QtbWFyaytqd3QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3RydXN0LW1hcmstaXNzdWVyLmZlZGVyYXRpb24ubG9jYWwiLCJzdWIiOiJodHRwczovL29wLmV4YW1wbGUub3JnIiwidHJ1c3RfbWFya190eXBlIjoiaHR0cHM6Ly9leGFtcGxlLm9yZy9hLXRydXN0LW1hcmsiLCJleHAiOjQ5MTgzNjczMzYsImlhdCI6MTc2NDc2NzMzNn0.smmtxeU_vCh2XFHLCxGHtwr_ZQ9A0-T7V9Poq5tNqwuU7_QlMAUJG1CJcprqQ9hH2oNSSQPIfUk7fOB1VUEY66U_bGBQ-KNQiIj-j25IQs7JalOCT1qjzcsMkq6i [...]
+                                </util:map>
+                            </constructor-arg>
+                        </bean>
+                    </util:list>
+                </property>
+            </bean>
+        </entry>
+    </util:map>
+
+</beans>
\ No newline at end of file

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


More information about the commits mailing list