[java-idp-oidc] branch dev/JOIDC-222 updated: JOIDC-222 - Support for OpenID Federation

Henri Mikkonen henri.mikkonen at iki.fi
Thu Mar 20 07:05:06 UTC 2025


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

hjmikkon pushed a commit to branch dev/JOIDC-222
in repository java-idp-oidc.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=d6e5a65b7a2811b05ff10965d52b0b130a702f3e

The following commit(s) were added to refs/heads/dev/JOIDC-222 by this push:
     new d6e5a65b JOIDC-222 - Support for OpenID Federation
d6e5a65b is described below

commit d6e5a65b7a2811b05ff10965d52b0b130a702f3e
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Thu Mar 20 09:04:52 2025 +0200

    JOIDC-222 - Support for OpenID Federation
    
    https://shibboleth.atlassian.net/browse/JOIDC-222
    
    - Refactor lookup functions dealing with RelyingPartyTrustChainContext
    - Add 'trust_anchor' and 'authority_hints' claims to the explicit registration response
---
 .../impl/BuildExplicitRegistrationResponse.java    | 57 ++++++++++++++++++++--
 ...> AbstractTrustChainContextLookupFunction.java} | 57 ++++++++++++----------
 ...DefaultSelectedTrustChainIDsLookupStrategy.java | 47 ++++--------------
 ...dTrustChainImmediateSuperiorLookupStrategy.java | 42 ++++++++++++++++
 ...ltSelectedTrustChainMetadataLookupStrategy.java | 41 ++--------------
 ...electedTrustChainTrustAnchorLookupStrategy.java | 41 ++++++++++++++++
 .../DefaultTrustChainSelectionStrategy.java        | 42 ++--------------
 .../profile/flow/oidfed/RegistrationFlowTest.java  | 15 ++++--
 8 files changed, 196 insertions(+), 146 deletions(-)

diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildExplicitRegistrationResponse.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildExplicitRegistrationResponse.java
index 368ea369..77742f8a 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildExplicitRegistrationResponse.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildExplicitRegistrationResponse.java
@@ -16,6 +16,7 @@ package net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl;
 
 import java.time.Instant;
 import java.util.Date;
+import java.util.List;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
@@ -33,11 +34,14 @@ import com.nimbusds.jwt.JWTClaimsSet;
 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.profile.navigate.DefaultSelectedTrustChainImmediateSuperiorLookupStrategy;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultSelectedTrustChainTrustAnchorLookupStrategy;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.ExplicitClientRegistrationRequestJWKSetLookupFunction;
 import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
 
 /**
  * An action that uses the information from {@link OIDCClientRegistrationResponseContext} attached to the message
@@ -59,6 +63,12 @@ public class BuildExplicitRegistrationResponse extends AbstractBuildEntityStatem
     /** Strategy used to lookup the trust chain context. */
     @Nonnull private Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextLookupStrategy;
 
+    /** Strategy used to lookup the trust anchor. */
+    @Nonnull private Function<ProfileRequestContext, String> trustAnchorLookupStrategy;
+
+    /** Strategy used to lookup the immediate superior used for the authority_hints claim. */
+    @Nonnull private Function<ProfileRequestContext, String> immediateSuperiorLookupStrategy;
+
     /** The {@link OIDCClientRegistrationResponseContext} to operate on. */
     @NonnullBeforeExec private OIDCClientRegistrationResponseContext oidcResponseContext;
 
@@ -78,6 +88,8 @@ public class BuildExplicitRegistrationResponse extends AbstractBuildEntityStatem
                         new InboundMessageContextLookup());
         assert tcls != null;
         trustChainContextLookupStrategy = tcls;
+        trustAnchorLookupStrategy = new DefaultSelectedTrustChainTrustAnchorLookupStrategy();
+        immediateSuperiorLookupStrategy = new DefaultSelectedTrustChainImmediateSuperiorLookupStrategy();
     }
     
     /**
@@ -115,6 +127,27 @@ public class BuildExplicitRegistrationResponse extends AbstractBuildEntityStatem
                 Constraint.isNotNull(strategy, "TrustChainContextLookupStrategy cannot be null");
     }
 
+    /**
+     * Set the strategy used to lookup the trust anchor.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setTrustAnchorLookupStrategy(@Nonnull final Function<ProfileRequestContext, String> strategy) {
+        checkSetterPreconditions();
+        trustAnchorLookupStrategy = Constraint.isNotNull(strategy, "TrustAnchorLookupStrategy cannot be null");
+    }
+
+    /**
+     * Set the strategy used to lookup the immediate superior used for the authority_hints claim.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setImmediateSuperiorLookupStrategy(@Nonnull final Function<ProfileRequestContext, String> strategy) {
+        checkSetterPreconditions();
+        immediateSuperiorLookupStrategy =
+                Constraint.isNotNull(strategy, "ImmediateSuperiorLookupStrategy cannot be null");
+    }
+
     /** {@inheritDoc} */
     @Override
     protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
@@ -131,8 +164,8 @@ public class BuildExplicitRegistrationResponse extends AbstractBuildEntityStatem
         }
 
         trustChainContext = trustChainContextLookupStrategy.apply(profileRequestContext);
-        if (trustChainContext == null || trustChainContext.getPolicyCompliantTrustChains() == null) {
-            log.error("{} Unable to locate policy-compliant trust chains", getLogPrefix());
+        if (trustChainContext == null) {
+            log.error("{} Unable to locate trust chain context", getLogPrefix());
             ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
             return false;
         }
@@ -152,6 +185,7 @@ public class BuildExplicitRegistrationResponse extends AbstractBuildEntityStatem
         }
         builder.claim("metadata",
                 CollectionSupport.singletonMap("openid_relying_party", clientInformation.toJSONObject()));
+
         final JWKSet jwkSet = jwkSetLookupStrategy.apply(profileRequestContext);
         if (jwkSet == null) {
             log.error("{} Coud not resolve JWK set to be included in the response statement", getLogPrefix());
@@ -159,6 +193,7 @@ public class BuildExplicitRegistrationResponse extends AbstractBuildEntityStatem
             return false;
         }
         builder.claim("jwks", jwkSet.toJSONObject(true));
+
         final Instant expirationTime = trustChainContext.getSelectedMetadataExpiration();
         if (expirationTime == null) {
             log.error("{} Coud not resolve expiration time from the selected trust chain context", getLogPrefix());
@@ -166,7 +201,23 @@ public class BuildExplicitRegistrationResponse extends AbstractBuildEntityStatem
             return false;
         }
         builder.expirationTime(Date.from(expirationTime));
+
+        final String trustAnchor = trustAnchorLookupStrategy.apply(profileRequestContext);
+        if (StringSupport.trimOrNull(trustAnchor) == null) {
+            log.error("{} Unable to locate trust anchor from the context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+            return false;
+        }
+        builder.claim("trust_anchor", trustAnchor);
+
+        final String superior = immediateSuperiorLookupStrategy.apply(profileRequestContext);
+        if (StringSupport.trimOrNull(superior) == null) {
+            log.error("{} Unable to locate immediate superior from the context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+            return false;
+        }
+        builder.claim("authority_hints", List.of(superior));
+
         return true;
     }
-
 }
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainMetadataLookupStrategy.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/AbstractTrustChainContextLookupFunction.java
similarity index 51%
copy from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainMetadataLookupStrategy.java
copy to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/AbstractTrustChainContextLookupFunction.java
index 6780b041..1c6bcb6a 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainMetadataLookupStrategy.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/AbstractTrustChainContextLookupFunction.java
@@ -14,7 +14,6 @@
 
 package net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate;
 
-import java.util.Optional;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
@@ -23,19 +22,21 @@ import javax.annotation.Nullable;
 import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
-
-import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
+import org.slf4j.Logger;
 
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.RelyingPartyTrustChainContext;
-import net.shibboleth.shared.annotation.ParameterName;
 import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
 
 /**
- * Default strategy for looking up the metadata the selected trust chain. The selected trust chain is fetched via
- * {@link RelyingPartyTrustChainContext#getSelectedTrustChain()}.
+ * An abstract function for lookup functions dealing with {@link RelyingPartyTrustChainContext}.
+ *
+ * @param <T> The return type of the lookup function
  */
-public class DefaultSelectedTrustChainMetadataLookupStrategy
-    implements Function<ProfileRequestContext,OIDCClientMetadata> {
+public abstract class AbstractTrustChainContextLookupFunction<T> implements Function<ProfileRequestContext, T> {
+
+    /** Class logger. */
+    @Nonnull private Logger log = LoggerFactory.getLogger(AbstractTrustChainContextLookupFunction.class);
 
     /** Strategy used to locate the trust chain context. */
     @Nonnull private Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextLookupStrategy;
@@ -43,34 +44,40 @@ public class DefaultSelectedTrustChainMetadataLookupStrategy
     /**
      * Constructor.
      */
-    public DefaultSelectedTrustChainMetadataLookupStrategy() {
+    public AbstractTrustChainContextLookupFunction() {
         final Function<ProfileRequestContext, RelyingPartyTrustChainContext> tcls =
-                new ChildContextLookup<>(RelyingPartyTrustChainContext.class, true).compose(
+                new ChildContextLookup<>(RelyingPartyTrustChainContext.class).compose(
                         new InboundMessageContextLookup());
         assert tcls != null;
         trustChainContextLookupStrategy = tcls;
     }
 
     /**
-     * Constructor.
-     *
-     * @param trustChainContextStrategy strategy used to locate the trust chain context
-     * @param trustChainIDsStrategy strategy used to get entity IDs from a trust chain
+     * Set the strategy used to locate the trust chain context.
+     * 
+     * @param strategy lookup strategy
      */
-    public DefaultSelectedTrustChainMetadataLookupStrategy(
-            @Nonnull @ParameterName(name = "trustChainContextLookupStrategy")
-            final Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextStrategy) {
+    public void setTrustChainContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, RelyingPartyTrustChainContext> strategy) {
         trustChainContextLookupStrategy =
-                Constraint.isNotNull(trustChainContextStrategy, "TrustChainContextLookupStrategy cannot be null");
+                Constraint.isNotNull(strategy, "TrustChainContextLookupStrategy cannot be null");
     }
 
     /** {@inheritDoc} */
-    @Override @Nullable
-    public OIDCClientMetadata apply(@Nullable final ProfileRequestContext input) {
-        return Optional.ofNullable(trustChainContextLookupStrategy.apply(input))
-                .map(trustChainContext -> trustChainContext.getSelectedTrustChain())
-                .map(pair -> pair.getSecond())
-                .map(clientInfo -> clientInfo.getOIDCMetadata())
-                .orElse(null);
+    @Override @Nullable public T apply(@Nullable final ProfileRequestContext input) {
+        final RelyingPartyTrustChainContext trustChainContext = trustChainContextLookupStrategy.apply(input);
+        if (trustChainContext == null) {
+            log.error("Could not resolve trust chain context");
+            return null;
+        }
+        return doApply(trustChainContext);
     }
+
+    /**
+     * Perform the lookup operation on the {@link RelyingPartyTrustChainContext}.
+     * 
+     * @param trustChainContext the context, guaranteed to be non-null
+     * @return result
+     */
+    @Nullable protected abstract T doApply(@Nonnull final RelyingPartyTrustChainContext trustChainContext);
 }
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainIDsLookupStrategy.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainIDsLookupStrategy.java
index 75b87dd8..8344b48c 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainIDsLookupStrategy.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainIDsLookupStrategy.java
@@ -21,29 +21,16 @@ import java.util.function.Function;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
-import org.slf4j.Logger;
-
 import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
 
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.RelyingPartyTrustChainContext;
-import net.shibboleth.shared.annotation.ParameterName;
 import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
 
 /**
  * Default strategy for looking up the entity IDs of the selected trust chain. The selected trust chain is fetched via
  * {@link RelyingPartyTrustChainContext#getSelectedTrustChain()}.
  */
-public class DefaultSelectedTrustChainIDsLookupStrategy implements Function<ProfileRequestContext,List<String>> {
-
-    /** Class logger. */
-    @Nonnull private Logger log = LoggerFactory.getLogger(DefaultSelectedTrustChainIDsLookupStrategy.class);
-
-    /** Strategy used to locate the trust chain context. */
-    @Nonnull private Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextLookupStrategy;
+public class DefaultSelectedTrustChainIDsLookupStrategy extends AbstractTrustChainContextLookupFunction<List<String>> {
 
     /** Strategy used to get entity IDs from a trust chain. */
     @Nonnull private Function<List<EntityStatement>, List<String>> trustChainIDsLookupStrategy;
@@ -52,39 +39,23 @@ public class DefaultSelectedTrustChainIDsLookupStrategy implements Function<Prof
      * Constructor.
      */
     public DefaultSelectedTrustChainIDsLookupStrategy() {
-        final Function<ProfileRequestContext, RelyingPartyTrustChainContext> tcls =
-                new ChildContextLookup<>(RelyingPartyTrustChainContext.class, true).compose(
-                        new InboundMessageContextLookup());
-        assert tcls != null;
-        trustChainContextLookupStrategy = tcls;
+        super();
         trustChainIDsLookupStrategy = new DefaultTrustChainIDsLookupStrategy();
     }
 
     /**
-     * Constructor.
-     *
-     * @param trustChainContextStrategy strategy used to locate the trust chain context
-     * @param trustChainIDsStrategy strategy used to get entity IDs from a trust chain
+     * Set the strategy used to get entity IDs from a trust chain.
+     * 
+     * @param strategy lookup strategy
      */
-    public DefaultSelectedTrustChainIDsLookupStrategy(
-            @Nonnull @ParameterName(name = "trustChainContextLookupStrategy")
-            final Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextStrategy,
-            @Nonnull @ParameterName(name = "trustChainIDsLookupStrategy")
-            final Function<List<EntityStatement>, List<String>> trustChainIDsStrategy) {
-        trustChainContextLookupStrategy =
-                Constraint.isNotNull(trustChainContextStrategy, "TrustChainContextLookupStrategy cannot be null");
-        trustChainIDsLookupStrategy =
-                Constraint.isNotNull(trustChainIDsStrategy, "TrustChainIDsLookupStrategy cannot be null");
+    public void setTrustChainIDsLookupStrategy(
+            @Nonnull final Function<List<EntityStatement>, List<String>> strategy) {
+        trustChainIDsLookupStrategy = Constraint.isNotNull(strategy, "TrustChainIDsLookupStrategy cannot be null");
     }
 
     /** {@inheritDoc} */
     @Override @Nullable
-    public List<String> apply(@Nullable final ProfileRequestContext input) {
-        final RelyingPartyTrustChainContext trustChainContext = trustChainContextLookupStrategy.apply(input);
-        if (trustChainContext == null || trustChainContext.getSelectedTrustChain() == null) {
-            log.debug("No selected trust chain located");
-            return null;
-        }
+    public List<String> doApply(@Nonnull final RelyingPartyTrustChainContext trustChainContext) {
         return Optional.ofNullable(trustChainContext.getSelectedTrustChain())
                 .map(pair -> pair.getFirst())
                 .map(chain -> trustChainIDsLookupStrategy.apply(chain))
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainImmediateSuperiorLookupStrategy.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainImmediateSuperiorLookupStrategy.java
new file mode 100644
index 00000000..f03a2a7d
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainImmediateSuperiorLookupStrategy.java
@@ -0,0 +1,42 @@
+/*
+ * 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.Optional;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.RelyingPartyTrustChainContext;
+
+/**
+ * Default strategy for looking up the entity ID of the immediate superior in the selected trust chain. The selected
+ * trust chain is fetched via {@link RelyingPartyTrustChainContext#getSelectedTrustChain()}.
+ */
+public class DefaultSelectedTrustChainImmediateSuperiorLookupStrategy
+    extends AbstractTrustChainContextLookupFunction<String> {
+
+    /** {@inheritDoc} */
+    @Override @Nullable
+    public String doApply(@Nonnull final RelyingPartyTrustChainContext trustChainContext) {
+        return Optional.ofNullable(trustChainContext.getSelectedTrustChain())
+                .map(pair -> pair.getFirst())
+                .filter(list -> list.size() >= 3)
+                .map(list -> list.get(1))
+                .map(entityStatement -> entityStatement.getClaimsSet().getIssuer())
+                .map(issuer -> issuer.getValue())
+                .orElse(null);
+    }
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainMetadataLookupStrategy.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainMetadataLookupStrategy.java
index 6780b041..205c2c6c 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainMetadataLookupStrategy.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainMetadataLookupStrategy.java
@@ -15,60 +15,25 @@
 package net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate;
 
 import java.util.Optional;
-import java.util.function.Function;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
-
 import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
 
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.RelyingPartyTrustChainContext;
-import net.shibboleth.shared.annotation.ParameterName;
-import net.shibboleth.shared.logic.Constraint;
 
 /**
  * Default strategy for looking up the metadata the selected trust chain. The selected trust chain is fetched via
  * {@link RelyingPartyTrustChainContext#getSelectedTrustChain()}.
  */
 public class DefaultSelectedTrustChainMetadataLookupStrategy
-    implements Function<ProfileRequestContext,OIDCClientMetadata> {
-
-    /** Strategy used to locate the trust chain context. */
-    @Nonnull private Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextLookupStrategy;
-
-    /**
-     * Constructor.
-     */
-    public DefaultSelectedTrustChainMetadataLookupStrategy() {
-        final Function<ProfileRequestContext, RelyingPartyTrustChainContext> tcls =
-                new ChildContextLookup<>(RelyingPartyTrustChainContext.class, true).compose(
-                        new InboundMessageContextLookup());
-        assert tcls != null;
-        trustChainContextLookupStrategy = tcls;
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param trustChainContextStrategy strategy used to locate the trust chain context
-     * @param trustChainIDsStrategy strategy used to get entity IDs from a trust chain
-     */
-    public DefaultSelectedTrustChainMetadataLookupStrategy(
-            @Nonnull @ParameterName(name = "trustChainContextLookupStrategy")
-            final Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextStrategy) {
-        trustChainContextLookupStrategy =
-                Constraint.isNotNull(trustChainContextStrategy, "TrustChainContextLookupStrategy cannot be null");
-    }
+    extends AbstractTrustChainContextLookupFunction<OIDCClientMetadata> {
 
     /** {@inheritDoc} */
     @Override @Nullable
-    public OIDCClientMetadata apply(@Nullable final ProfileRequestContext input) {
-        return Optional.ofNullable(trustChainContextLookupStrategy.apply(input))
-                .map(trustChainContext -> trustChainContext.getSelectedTrustChain())
+    public OIDCClientMetadata doApply(@Nonnull final RelyingPartyTrustChainContext trustChainContext) {
+        return Optional.ofNullable(trustChainContext.getSelectedTrustChain())
                 .map(pair -> pair.getSecond())
                 .map(clientInfo -> clientInfo.getOIDCMetadata())
                 .orElse(null);
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainTrustAnchorLookupStrategy.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainTrustAnchorLookupStrategy.java
new file mode 100644
index 00000000..ae644f2b
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainTrustAnchorLookupStrategy.java
@@ -0,0 +1,41 @@
+/*
+ * 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.Optional;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.RelyingPartyTrustChainContext;
+
+/**
+ * Default strategy for looking up the entity ID of the trust anchor in the selected trust chain. The selected trust
+ * chain is fetched via {@link RelyingPartyTrustChainContext#getSelectedTrustChain()}.
+ */
+public class DefaultSelectedTrustChainTrustAnchorLookupStrategy
+    extends AbstractTrustChainContextLookupFunction<String> {
+
+    /** {@inheritDoc} */
+    @Override @Nullable
+    public String doApply(@Nonnull final RelyingPartyTrustChainContext trustChainContext) {
+        return Optional.ofNullable(trustChainContext.getSelectedTrustChain())
+                .map(pair -> pair.getFirst())
+                .filter(list -> list.size() >= 3)
+                .map(list -> list.get(list.size() - 1))
+                .map(entityStatement -> entityStatement.getEntityID().getValue())
+                .orElse(null);
+    }
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustChainSelectionStrategy.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustChainSelectionStrategy.java
index 1e3b84a1..3bbe1cd5 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustChainSelectionStrategy.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultTrustChainSelectionStrategy.java
@@ -15,14 +15,10 @@
 package net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate;
 
 import java.util.List;
-import java.util.function.Function;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
 import org.slf4j.Logger;
 
 import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
@@ -30,7 +26,6 @@ import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
 
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.RelyingPartyTrustChainContext;
 import net.shibboleth.shared.collection.Pair;
-import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
 
 /**
@@ -38,45 +33,16 @@ import net.shibboleth.shared.primitive.LoggerFactory;
  * shortest. The selection must not be included in the list of previously rejected trust chains, obtained via
  * {@link RelyingPartyTrustChainContext#getRejectedTrustChains()}.
  */
-public class DefaultTrustChainSelectionStrategy implements
-        Function<ProfileRequestContext,Pair<List<EntityStatement>, OIDCClientInformation>> {
+public class DefaultTrustChainSelectionStrategy
+    extends AbstractTrustChainContextLookupFunction<Pair<List<EntityStatement>, OIDCClientInformation>> {
 
     /** Class logger. */
     @Nonnull private Logger log = LoggerFactory.getLogger(DefaultTrustChainSelectionStrategy.class);
 
-    /** Strategy used to locate the trust chain context. */
-    @Nonnull private Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextLookupStrategy;
-
-    /**
-     * Constructor.
-     */
-    public DefaultTrustChainSelectionStrategy() {
-        final Function<ProfileRequestContext, RelyingPartyTrustChainContext> tcls =
-                new ChildContextLookup<>(RelyingPartyTrustChainContext.class, true).compose(
-                        new InboundMessageContextLookup());
-        assert tcls != null;
-        trustChainContextLookupStrategy = tcls;
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param strategy the strategy used to locate the trust chain context
-     */
-    public DefaultTrustChainSelectionStrategy(
-            @Nonnull final Function<ProfileRequestContext, RelyingPartyTrustChainContext> strategy) {
-        trustChainContextLookupStrategy =
-                Constraint.isNotNull(strategy, "TrustChainContextLookupStrategy cannot be null");
-    }
-
     /** {@inheritDoc} */
     @Override @Nullable
-    public Pair<List<EntityStatement>, OIDCClientInformation> apply(@Nullable final ProfileRequestContext input) {
-        final RelyingPartyTrustChainContext trustChainContext = trustChainContextLookupStrategy.apply(input);
-        if (trustChainContext == null) {
-            log.debug("No trust chain context located");
-            return null;
-        }
+    public Pair<List<EntityStatement>, OIDCClientInformation> doApply(
+            @Nonnull final RelyingPartyTrustChainContext trustChainContext) {
         final List<Pair<List<EntityStatement>, OIDCClientInformation>> policyCompliantChains =
                 trustChainContext.getPolicyCompliantTrustChains();
 
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/RegistrationFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/RegistrationFlowTest.java
index f607eec4..c41d1361 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/RegistrationFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/RegistrationFlowTest.java
@@ -14,6 +14,8 @@
 
 package net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed;
 
+import java.util.List;
+
 import org.opensaml.storage.StorageRecord;
 import org.opensaml.storage.StorageService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -23,6 +25,7 @@ import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatementClaimsSet;
 import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
 import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
 
@@ -78,13 +81,17 @@ public class RegistrationFlowTest extends AbstractFederationFlowTest {
         final ExplicitClientRegistrationResponse parsedResponse =
                 parseSuccessResponse(result, ExplicitClientRegistrationResponse.class);
         final EntityStatement entityStatement = parsedResponse.getEntityStatement();
+        final EntityStatementClaimsSet statementClaims = entityStatement.getClaimsSet();
+        Assert.assertEquals(statementClaims.getAuthorityHints().stream().map(id -> id.getValue()).toList(),
+                List.of(anchorId));
+        Assert.assertEquals(statementClaims.getClaim("trust_anchor"), anchorId);
         final OIDCClientInformation clientInfo = entityStatement.getClaimsSet().getRPInformation();
         final OIDCClientMetadata metadata = clientInfo.getOIDCMetadata();
         final String providedClientId = clientInfo.getID().getValue();
         assert providedClientId != null;
         assert storageService != null;
-        final StorageRecord<String> storageRecord = storageService.read(BaseStorageServiceClientInformationComponent.CONTEXT_NAME, 
-                providedClientId);
+        final StorageRecord<String> storageRecord =
+                storageService.read(BaseStorageServiceClientInformationComponent.CONTEXT_NAME, providedClientId);
         Assert.assertNotNull(storageRecord, "Record with clientId " + providedClientId + " was null");
         assert storageRecord != null;
         final String record = storageRecord.getValue();
@@ -93,8 +100,8 @@ public class RegistrationFlowTest extends AbstractFederationFlowTest {
         final OIDCClientInformation storedInfo = OIDCClientInformation.parse((JSONObject) parser.parse(record));
         Assert.assertEquals(storedInfo.getID(), clientInfo.getID());
         Assert.assertEquals(storedInfo.getSecret(), clientInfo.getSecret());
-        Assert.assertEquals(storedInfo.getOIDCMetadata().getRedirectionURIStrings(), metadata.getRedirectionURIStrings());
-        //Assert.assertEquals(storedInfo.getOIDCMetadata().getRequestObjectURIs(), Set.of(new URI(requestUri)));
+        Assert.assertEquals(storedInfo.getOIDCMetadata().getRedirectionURIStrings(),
+                metadata.getRedirectionURIStrings());
         Assert.assertTrue(metadata.getRedirectionURIStrings().contains(redirectUri));
         
     }

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


More information about the commits mailing list