[java-identity-provider] branch main updated: IDP-2319- Login fows should support a dedicated c14n list

Scott Cantor cantor.2 at osu.edu
Fri Feb 21 20:43:27 UTC 2025


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

scantor pushed a commit to branch main
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=175afe25ac8bf234e1a22c0d22599ed8975a479f

The following commit(s) were added to refs/heads/main by this push:
     new 175afe25a IDP-2319- Login fows should support a dedicated c14n list
175afe25a is described below

commit 175afe25ac8bf234e1a22c0d22599ed8975a479f
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Fri Feb 21 15:43:24 2025 -0500

    IDP-2319- Login fows should support a dedicated c14n list
    
    https://shibboleth.atlassian.net/browse/IDP-2319
    
    Auto-wire post login flows.
    Differentiate flow ID from descriptor bean ID.
    Add per-login flow property hook to control list of beans to run.
---
 ...LoginSubjectCanonicalizationFlowDescriptor.java | 29 ++++++++
 .../SubjectCanonicalizationFlowDescriptor.java     | 70 ++++++++++++++++---
 .../context/SubjectCanonicalizationContext.java    |  4 +-
 .../PopulateSubjectCanonicalizationContext.java    | 81 +++++++++++++++++++---
 ...bjectCanonicalizationFlowDescriptorManager.java | 45 ++++++++++++
 .../impl/SelectSubjectCanonicalizationFlow.java    | 32 +++++----
 .../shibboleth/idp/conf/subject-c14n-system.xml    |  8 ++-
 .../idp/flows/authn/external-authn-beans.xml       |  5 +-
 .../idp/flows/authn/function-authn-beans.xml       |  5 +-
 .../idp/flows/authn/ipaddress-authn-beans.xml      |  5 +-
 .../shibboleth/idp/flows/authn/mfa-authn-beans.xml |  6 +-
 .../idp/flows/authn/password-authn-beans.xml       |  6 +-
 .../idp/flows/authn/remoteuser-authn-beans.xml     |  5 +-
 .../authn/remoteuser-internal-authn-beans.xml      |  5 +-
 .../idp/flows/authn/saml-authn-beans.xml           |  5 +-
 .../idp/flows/authn/spnego-authn-beans.xml         |  5 +-
 .../idp/flows/authn/x509-authn-beans.xml           |  5 +-
 .../idp/flows/authn/x509-internal-authn-beans.xml  |  5 +-
 .../idp/module/conf/authn/authn.properties         | 25 ++++++-
 19 files changed, 303 insertions(+), 48 deletions(-)

diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/PostLoginSubjectCanonicalizationFlowDescriptor.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/PostLoginSubjectCanonicalizationFlowDescriptor.java
new file mode 100644
index 000000000..7c3ff35d2
--- /dev/null
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/PostLoginSubjectCanonicalizationFlowDescriptor.java
@@ -0,0 +1,29 @@
+/*
+ * 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.authn;
+
+import javax.security.auth.Subject;
+
+/**
+ * Marker subtype for a {@link SubjectCanonicalizationFlowDescriptor} that applies to the post-login
+ * scenario of transforming the {@link Subject} produced for an {@link AuthenticationResult} into
+ * a final name.
+ * 
+ * @since 5.2.0
+ */
+public class PostLoginSubjectCanonicalizationFlowDescriptor extends SubjectCanonicalizationFlowDescriptor {
+
+
+}
\ No newline at end of file
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/SubjectCanonicalizationFlowDescriptor.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/SubjectCanonicalizationFlowDescriptor.java
index 767df892d..0478fe962 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/SubjectCanonicalizationFlowDescriptor.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/SubjectCanonicalizationFlowDescriptor.java
@@ -17,15 +17,20 @@ package net.shibboleth.idp.authn;
 import java.util.function.Predicate;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.opensaml.profile.context.ProfileRequestContext;
 
 import com.google.common.base.MoreObjects;
 
+import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
 import net.shibboleth.idp.profile.FlowDescriptor;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.logic.PredicateSupport;
+import net.shibboleth.shared.primitive.StringSupport;
 
 /**
  * A descriptor for a subject canonicalization flow.
@@ -33,14 +38,16 @@ import net.shibboleth.shared.logic.PredicateSupport;
  * <p>
  * A flow models a sequence of profile actions that performs canonicalization of a {@link javax.security.auth.Subject}
  * into a string-form principal name. Flows can do essentially anything, including interact with the subject, but must
- * include an activation predicate to indicate their suitability based on the content of the
- * {@link ProfileRequestContext}, particularly the required
- * {@link net.shibboleth.idp.authn.context.SubjectCanonicalizationContext} child context.
+ * include an activation predicate to indicate their suitability based on the content of the context tree, particularly
+ * the required {@link SubjectCanonicalizationContext} child context.
  * </p>
  */
 public class SubjectCanonicalizationFlowDescriptor extends AbstractIdentifiableInitializableComponent implements
         FlowDescriptor, Predicate<ProfileRequestContext> {
 
+    /** Optional flow ID that may differ from component ID. */
+    @NonnullAfterInit private String flowId;
+    
     /** Predicate that must be true for this flow to be usable for a given request. */
     @Nonnull private Predicate<ProfileRequestContext> activationCondition;
 
@@ -48,7 +55,34 @@ public class SubjectCanonicalizationFlowDescriptor extends AbstractIdentifiableI
     public SubjectCanonicalizationFlowDescriptor() {
         activationCondition = PredicateSupport.alwaysTrue();
     }
+    
+    /**
+     * Gets the flow ID for this descriptor (inclusive of the c14n/ prefix).
+     * 
+     * @return flow ID
+     */
+    @NonnullAfterInit public String getFlowId() {
+        return flowId;
+    }
 
+    /**
+     * Sets the flow ID for this descriptor.
+     * 
+     * <p>This defaults to the component ID, but can be overridden to allow multiple
+     * components to be defined for a given webflow.</p>
+     * 
+     * @param id
+     * 
+     * @since 5.2.0
+     */
+    public void setFlowId(@Nullable final String id) {
+        checkSetterPreconditions();
+        flowId = StringSupport.trimOrNull(id);
+        if (id != null) {
+            Constraint.isFalse(flowId.startsWith("c14n/"), "Flow ID must begin with c14n/ prefix.");
+        }
+    }
+    
     /**
      * Set the activation condition in the form of a {@link Predicate} such that iff the condition evaluates to true
      * should the corresponding flow be allowed/possible.
@@ -59,19 +93,36 @@ public class SubjectCanonicalizationFlowDescriptor extends AbstractIdentifiableI
         checkSetterPreconditions();
         activationCondition = Constraint.isNotNull(condition, "Activation condition predicate cannot be null");
     }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        // Backfill flow ID with component ID.
+        if (flowId == null) {
+            flowId = ensureId();
+            if (!flowId.startsWith("c14n/")) {
+                throw new ComponentInitializationException("Defaulted flow ID must begin with c14n/ prefix.");
+            }
+        }
+    }
 
     /** {@inheritDoc} */
-    @Override public boolean test(final ProfileRequestContext input) {
+    public boolean test(final ProfileRequestContext input) {
+        checkComponentActive();
         return activationCondition.test(input);
     }
     
     /** {@inheritDoc} */
-    @Override public int hashCode() {
+    @Override
+    public int hashCode() {
         return getId().hashCode();
     }
 
     /** {@inheritDoc} */
-    @Override public boolean equals(final Object obj) {
+    @Override
+    public boolean equals(final Object obj) {
         if (obj == null) {
             return false;
         }
@@ -80,15 +131,16 @@ public class SubjectCanonicalizationFlowDescriptor extends AbstractIdentifiableI
             return true;
         }
 
-        if (obj instanceof SubjectCanonicalizationFlowDescriptor) {
-            return getId().equals(((SubjectCanonicalizationFlowDescriptor) obj).getId());
+        if (obj instanceof SubjectCanonicalizationFlowDescriptor scfd) {
+            return getId().equals(scfd.getId());
         }
 
         return false;
     }
 
     /** {@inheritDoc} */
-    @Override public String toString() {
+    @Override
+    public String toString() {
         return MoreObjects.toStringHelper(this).add("flowId", getId()).toString();
     }
 
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/SubjectCanonicalizationContext.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/SubjectCanonicalizationContext.java
index 6d8671007..6b31e18de 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/SubjectCanonicalizationContext.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/SubjectCanonicalizationContext.java
@@ -53,10 +53,10 @@ public final class SubjectCanonicalizationContext extends BaseContext {
     @Nullable private String responderId;
 
     /** Flows that could potentially be used. */
-    @Nonnull private final Map<String, SubjectCanonicalizationFlowDescriptor> potentialFlows;
+    @Nonnull private final Map<String,SubjectCanonicalizationFlowDescriptor> potentialFlows;
 
     /** Previously attempted flows (could be failures or intermediate results). */
-    @Nonnull private final Map<String, SubjectCanonicalizationFlowDescriptor> intermediateFlows;
+    @Nonnull private final Map<String,SubjectCanonicalizationFlowDescriptor> intermediateFlows;
     
     /** The last c14 flow attempted. */
     @Nullable private SubjectCanonicalizationFlowDescriptor attemptedFlow;
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/PopulateSubjectCanonicalizationContext.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/PopulateSubjectCanonicalizationContext.java
index ac673e01d..6af7a86ef 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/PopulateSubjectCanonicalizationContext.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/PopulateSubjectCanonicalizationContext.java
@@ -15,8 +15,12 @@
 package net.shibboleth.idp.authn.impl;
 
 import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
@@ -27,13 +31,19 @@ import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.shared.spring.config.IdentifiedComponentManager;
 
 /**
  * An action that populates a {@link SubjectCanonicalizationContext} with the
- * {@link SubjectCanonicalizationFlowDescriptor} objects configured into the IdP.
+ * {@link SubjectCanonicalizationFlowDescriptor} objects configured into the IdP,
+ * optionally along with the actual descriptor IDs to try in a particular order.
+ * 
+ * <p>The latter set filters the former and controls the order of insertion
+ * into the context.</p>
  * 
  * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
- * @pre <pre>ProfileRequestContext.getSubcontext(SubjectCanonicalizationContext.class, false) != null</pre>
+ * @pre <pre>ProfileRequestContext.getSubcontext(SubjectCanonicalizationContext.class) != null</pre>
  * @post The SubjectCanonicalizationContext is modified as above.
  */
 public class PopulateSubjectCanonicalizationContext extends AbstractSubjectCanonicalizationAction {
@@ -42,11 +52,15 @@ public class PopulateSubjectCanonicalizationContext extends AbstractSubjectCanon
     @Nonnull private final Logger log = LoggerFactory.getLogger(PopulateSubjectCanonicalizationContext.class);
     
     /** The flows to make available for possible use. */
-    @Nonnull private Collection<SubjectCanonicalizationFlowDescriptor> availableFlows;
+    @Nonnull private Map<String,SubjectCanonicalizationFlowDescriptor> availableFlows;
+    
+    /** This is the actual set of descriptor component IDs to install for use in their designated order. */
+    @Nonnull private Collection<String> usableFlows;
 
     /** Constructor. */
     PopulateSubjectCanonicalizationContext() {
-        availableFlows = CollectionSupport.emptyList();
+        availableFlows = new LinkedHashMap<>();
+        usableFlows = CollectionSupport.emptyList();
     }
     
     /**
@@ -56,18 +70,67 @@ public class PopulateSubjectCanonicalizationContext extends AbstractSubjectCanon
      */
     public void setAvailableFlows(@Nonnull final Collection<SubjectCanonicalizationFlowDescriptor> flows) {
         checkSetterPreconditions();
-        availableFlows = CollectionSupport.copyToList(Constraint.isNotNull(flows, "Flow collection cannot be null"));
+
+        Constraint.isNotNull(flows, "Flow collection cannot be null").forEach(fd -> {
+            availableFlows.put(fd.ensureId(), fd);
+        });
     }
+
+    /**
+     * Set the flows available for possible use via a manager object.
+     * 
+     * <p>Use of this variant setter should be accompanied by use of the {@link #setUsableFlows(List)} method
+     * introduced to filter the eventual list.</p> 
+     * 
+     * @param manager the flows available for possible use
+     * 
+     * @since 5.2.0
+     */
+    public void setAvailableFlows(
+            @Nonnull final IdentifiedComponentManager<SubjectCanonicalizationFlowDescriptor> manager) {
+        checkSetterPreconditions();
         
+        manager.getComponents().forEach(fd -> {
+            availableFlows.put(fd.ensureId(), fd);
+        });
+    }
+
+    /**
+     * Set an optional ordered list of flow descriptor IDs that should filter the available flows and
+     * provide an alternative order of use from the injected collection's implied order.
+     * 
+     * @param usable {@link SubjectCanonicalizationFlowDescriptor} IDs, in ordered list of use
+     * 
+     * @since 5.2.0
+     */
+    public void setUsableFlows(@Nullable final List<String> usable) {
+        checkSetterPreconditions();
+        // This method does guarantee order based on the source.
+        usableFlows = StringSupport.normalizeStringCollection(usable);
+    }
+    
     /** {@inheritDoc} */
     @Override
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
             @Nonnull final SubjectCanonicalizationContext c14nContext) {
 
-        log.debug("{} Installing {} canonicalization flows into SubjectCanonicalizationContext", getLogPrefix(),
-                availableFlows.size());
-        for (final SubjectCanonicalizationFlowDescriptor desc : availableFlows) {
-            c14nContext.getPotentialFlows().put(desc.ensureId(), desc);
+        if (usableFlows.isEmpty()) {
+            log.debug("{} Installing {} c14n flows into SubjectCanonicalizationContext", getLogPrefix(),
+                    availableFlows.size());
+            // It is presumed (and we need for this) to maintain order.
+            c14nContext.getPotentialFlows().putAll(availableFlows);
+        } else {
+            for (final String id : usableFlows) {
+                final var fd = availableFlows.get(id);
+                if (fd != null) {
+                    c14nContext.getPotentialFlows().put(id, fd);
+                } else {
+                    log.warn("{} Skipping installation of flow descriptor '{}', not available in configuration?",
+                            getLogPrefix(), id);
+                }
+            }
+            log.debug("{} Installed {} c14n flows into SubjectCanonicalizationContext", getLogPrefix(),
+                    c14nContext.getPotentialFlows().size());
         }
     }
     
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/PostLoginSubjectCanonicalizationFlowDescriptorManager.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/PostLoginSubjectCanonicalizationFlowDescriptorManager.java
new file mode 100644
index 000000000..cac472d22
--- /dev/null
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/PostLoginSubjectCanonicalizationFlowDescriptorManager.java
@@ -0,0 +1,45 @@
+/*
+ * 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.authn.impl;
+
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import net.shibboleth.idp.authn.PostLoginSubjectCanonicalizationFlowDescriptor;
+import net.shibboleth.shared.spring.config.IdentifiedComponentManager;
+
+/**
+ * Manager of {@link PostLoginSubjectCanonicalizationFlowDescriptor} objects.
+ * 
+ * @since 5.2.0
+ */
+public class PostLoginSubjectCanonicalizationFlowDescriptorManager
+        extends IdentifiedComponentManager<PostLoginSubjectCanonicalizationFlowDescriptor> {
+
+    /**
+     * Constructor.
+     *
+     * @param freeObjects  free-standing objects
+     */
+    @Autowired
+    public PostLoginSubjectCanonicalizationFlowDescriptorManager(
+            @Nullable final List<PostLoginSubjectCanonicalizationFlowDescriptor> freeObjects) {
+        super(freeObjects);
+    }
+
+}
\ No newline at end of file
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SelectSubjectCanonicalizationFlow.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SelectSubjectCanonicalizationFlow.java
index c72a36dad..3183e4922 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SelectSubjectCanonicalizationFlow.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/SelectSubjectCanonicalizationFlow.java
@@ -41,7 +41,7 @@ import net.shibboleth.shared.primitive.LoggerFactory;
  * 
  * @event {@link AuthnEventIds#NO_POTENTIAL_FLOW}
  * @event Selected flow ID to execute
- * @pre <pre>ProfileRequestContext.getSubcontext(SubjectCanonicalizationContext.class, false) != null</pre>
+ * @pre <pre>ProfileRequestContext.getSubcontext(SubjectCanonicalizationContext.class) != null</pre>
  */
 public class SelectSubjectCanonicalizationFlow extends AbstractSubjectCanonicalizationAction {
 
@@ -53,17 +53,20 @@ public class SelectSubjectCanonicalizationFlow extends AbstractSubjectCanonicali
     protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
             @Nonnull final SubjectCanonicalizationContext c14nContext) {
         
+        if (!super.doPreExecute(profileRequestContext, c14nContext)) {
+            return false;
+        }
+        
         // Detect a previous attempted flow, and move it to the intermediate collection.
         // This will prevent re-selecting the same (probably failed) flow again.
-        final SubjectCanonicalizationFlowDescriptor flow = c14nContext.getAttemptedFlow(); 
+        final SubjectCanonicalizationFlowDescriptor flow = c14nContext.getAttemptedFlow();
         if (flow != null) {
-            log.info("{} Moving incomplete flow {} to intermediate set, reselecting a different one", getLogPrefix(),
-                    flow.getId());
-            c14nContext.getIntermediateFlows().put(
-                    flow.ensureId(), c14nContext.getAttemptedFlow());
+            log.info("{} Moving incomplete c14n flow descriptor {} to intermediate set, reselecting different one",
+                    getLogPrefix(), flow.getId());
+            c14nContext.getIntermediateFlows().put(flow.ensureId(), c14nContext.getAttemptedFlow());
         }
         
-        return super.doPreExecute(profileRequestContext, c14nContext);
+        return true;
     }
     
     /** {@inheritDoc} */
@@ -73,12 +76,13 @@ public class SelectSubjectCanonicalizationFlow extends AbstractSubjectCanonicali
         
         final SubjectCanonicalizationFlowDescriptor flow = selectUnattemptedFlow(profileRequestContext, c14nContext);
         if (flow == null) {
-            log.error("{} No potential flows left to choose from, canonicalization will fail", getLogPrefix());
+            log.error("{} No potential flows left to choose from, c14n will fail", getLogPrefix());
             ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_POTENTIAL_FLOW);
             return;
         }
-        log.debug("{} Selecting canonicalization flow {}", getLogPrefix(), flow.ensureId());
-        ActionSupport.buildEvent(profileRequestContext, flow.ensureId());
+        log.debug("{} Selecting c14n descriptor {} (WebFlow ID: {})", getLogPrefix(), flow.ensureId(),
+                flow.getFlowId());
+        ActionSupport.buildEvent(profileRequestContext, flow.getFlowId());
     }
 
     /**
@@ -92,18 +96,18 @@ public class SelectSubjectCanonicalizationFlow extends AbstractSubjectCanonicali
     @Nullable private SubjectCanonicalizationFlowDescriptor selectUnattemptedFlow(
             @Nonnull final ProfileRequestContext profileRequestContext,
             @Nonnull final SubjectCanonicalizationContext c14nContext) {
+        
         for (final SubjectCanonicalizationFlowDescriptor flow : c14nContext.getPotentialFlows().values()) {
             if (!c14nContext.getIntermediateFlows().containsKey(flow.ensureId())) {
-                log.debug("{} Checking canonicalization flow {} for applicability...", getLogPrefix(),
+                log.debug("{} Checking c14n flow descriptor {} for applicability...", getLogPrefix(),
                         flow.getId());
                 c14nContext.setAttemptedFlow(flow);
                 if (flow.test(profileRequestContext)) {
                     return flow;
                 }
                 final Exception ctxException = c14nContext.getException();
-                log.debug("{} Canonicalization flow {} was not applicable: {}", getLogPrefix(), flow.getId(),
-                        ctxException!= null ? ctxException.getMessage()
-                                : "reason unknown");
+                log.debug("{} C14N flow descriptor {} was not applicable: {}", getLogPrefix(), flow.getId(),
+                        ctxException!= null ? ctxException.getMessage() : "reason unknown");
                 c14nContext.setException(null);
                 
                 // Note that we don't exclude this flow from possible future selection, since one flow
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/subject-c14n-system.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/subject-c14n-system.xml
index 4a7c06236..d6276c3fb 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/subject-c14n-system.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/conf/subject-c14n-system.xml
@@ -12,6 +12,12 @@
        default-init-method="initialize"
        default-destroy-method="destroy">
 
+    <!-- Management bean to collect and expose Post-Login Subject C14N flow descriptors. -->
+    
+    <bean id="shibboleth.PostLoginC14NFlowDescriptorManager"
+        class="net.shibboleth.idp.authn.impl.PostLoginSubjectCanonicalizationFlowDescriptorManager"
+        p:components="#{getObject('shibboleth.PostLoginSubjectCanonicalizationFlows')}" />
+
     <!-- Dummy beans expected to be redefined by imported resource. -->
     <bean id="shibboleth.ProxyNameTransformPredicate" parent="shibboleth.Conditions.FALSE" />
     <util:list id="shibboleth.ProxyNameTransformFormats" />
@@ -19,7 +25,7 @@
     <import resource="${idp.home}/conf/c14n/subject-c14n.xml" />
 
     <bean id="shibboleth.PostLoginSubjectCanonicalizationFlow" abstract="true"
-        class="net.shibboleth.idp.authn.SubjectCanonicalizationFlowDescriptor" />
+        class="net.shibboleth.idp.authn.PostLoginSubjectCanonicalizationFlowDescriptor" />
 
     <bean id="c14n/simple" parent="shibboleth.PostLoginSubjectCanonicalizationFlow">
         <property name="activationCondition">
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/external-authn-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/external-authn-beans.xml
index 7b07647a1..66e00d442 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/external-authn-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/external-authn-beans.xml
@@ -29,9 +29,12 @@
         p:populateAuditContextAction="#{%{idp.authn.External.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('shibboleth.authn.External.PopulateAuditContext') : null}"
         p:writeAuditLogAction="#{%{idp.authn.External.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('WriteAuthnAuditLog') : null}" />
 
+    <bean id="UsableC14NFlows" parent="shibboleth.CommaDelimStringArray" c:_0="%{idp.authn.External.c14n.flows:}" />
+    
     <bean id="PopulateSubjectCanonicalizationContext"
         class="net.shibboleth.idp.authn.impl.PopulateSubjectCanonicalizationContext" scope="prototype"
-        p:availableFlows-ref="shibboleth.PostLoginSubjectCanonicalizationFlows" />
+        p:availableFlows-ref="shibboleth.PostLoginC14NFlowDescriptorManager"
+        p:usableFlows-ref="UsableC14NFlows" />
 
     <!-- Audit logging beans. -->
 
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/function-authn-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/function-authn-beans.xml
index 51338b0ab..0e4f3efcd 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/function-authn-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/function-authn-beans.xml
@@ -28,9 +28,12 @@
         p:populateAuditContextAction="#{%{idp.authn.Function.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('shibboleth.authn.Function.PopulateAuditContext') : null}"
         p:writeAuditLogAction="#{%{idp.authn.Function.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('WriteAuthnAuditLog') : null}" />
 
+    <bean id="UsableC14NFlows" parent="shibboleth.CommaDelimStringArray" c:_0="%{idp.authn.Function.c14n.flows:}" />
+    
     <bean id="PopulateSubjectCanonicalizationContext"
         class="net.shibboleth.idp.authn.impl.PopulateSubjectCanonicalizationContext" scope="prototype"
-        p:availableFlows-ref="shibboleth.PostLoginSubjectCanonicalizationFlows" />
+        p:availableFlows-ref="shibboleth.PostLoginC14NFlowDescriptorManager"
+        p:usableFlows-ref="UsableC14NFlows" />
 
     <!-- Audit logging beans. -->
 
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/ipaddress-authn-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/ipaddress-authn-beans.xml
index de6966a77..f121bfad7 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/ipaddress-authn-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/ipaddress-authn-beans.xml
@@ -27,9 +27,12 @@
         p:populateAuditContextAction="#{%{idp.authn.IPAddress.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('shibboleth.authn.IPAddress.PopulateAuditContext') : null}"
         p:writeAuditLogAction="#{%{idp.authn.IPAddress.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('WriteAuthnAuditLog') : null}" />
 
+    <bean id="UsableC14NFlows" parent="shibboleth.CommaDelimStringArray" c:_0="%{idp.authn.IPAddress.c14n.flows:}" />
+    
     <bean id="PopulateSubjectCanonicalizationContext"
         class="net.shibboleth.idp.authn.impl.PopulateSubjectCanonicalizationContext" scope="prototype"
-        p:availableFlows-ref="shibboleth.PostLoginSubjectCanonicalizationFlows" />
+        p:availableFlows-ref="shibboleth.PostLoginC14NFlowDescriptorManager"
+        p:usableFlows-ref="UsableC14NFlows" />
 
     <!-- Audit logging beans. -->
 
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/mfa-authn-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/mfa-authn-beans.xml
index 6f8f7db54..20af79008 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/mfa-authn-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/mfa-authn-beans.xml
@@ -37,7 +37,11 @@
         class="net.shibboleth.idp.authn.impl.FinalizeMultiFactorAuthentication.DefaultResultMergingStrategy"
         p:useLatestTimestamp="%{idp.authn.MFA.useLatestTimestamp:false}" />
 
+    <bean id="UsableC14NFlows" parent="shibboleth.CommaDelimStringArray" c:_0="%{idp.authn.MFA.c14n.flows:}" />
+    
     <bean id="PopulateSubjectCanonicalizationContext"
         class="net.shibboleth.idp.authn.impl.PopulateSubjectCanonicalizationContext" scope="prototype"
-        p:availableFlows-ref="shibboleth.PostLoginSubjectCanonicalizationFlows" />
+        p:availableFlows-ref="shibboleth.PostLoginC14NFlowDescriptorManager"
+        p:usableFlows-ref="UsableC14NFlows" />
+
 </beans>
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/password-authn-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/password-authn-beans.xml
index e2a137b9e..eb489d61f 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/password-authn-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/password-authn-beans.xml
@@ -69,9 +69,13 @@
         p:passwordFieldName="#{getObject('shibboleth.authn.Password.PasswordFieldName') ?: '%{idp.authn.Password.passwordFieldName:j_password}'.trim()}"
         p:SSOBypassFieldName="#{getObject('shibboleth.authn.Password.SSOBypassFieldName') ?: '%{idp.authn.Password.ssoBypassFieldName:donotcache}'.trim()}" />
         
+    
+    <bean id="UsableC14NFlows" parent="shibboleth.CommaDelimStringArray" c:_0="%{idp.authn.Password.c14n.flows:}" />
+    
     <bean id="PopulateSubjectCanonicalizationContext"
         class="net.shibboleth.idp.authn.impl.PopulateSubjectCanonicalizationContext" scope="prototype"
-        p:availableFlows-ref="shibboleth.PostLoginSubjectCanonicalizationFlows" />
+        p:availableFlows-ref="shibboleth.PostLoginC14NFlowDescriptorManager"
+        p:usableFlows-ref="UsableC14NFlows" />
     
     <bean id="DefaultCleanupHook" class="net.shibboleth.idp.authn.impl.ValidateCredentials.UsernamePasswordCleanupHook"
         p:dataSealer="#{'%{idp.authn.usernameCookieName:}'.trim().isEmpty() ? null : getObject('shibboleth.DataSealer')}"
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/remoteuser-authn-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/remoteuser-authn-beans.xml
index 807eb9c49..7ea37d60f 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/remoteuser-authn-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/remoteuser-authn-beans.xml
@@ -29,9 +29,12 @@
         p:populateAuditContextAction="#{%{idp.authn.RemoteUser.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('shibboleth.authn.RemoteUser.PopulateAuditContext') : null}"
         p:writeAuditLogAction="#{%{idp.authn.RemoteUser.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('WriteAuthnAuditLog') : null}" />
 
+    <bean id="UsableC14NFlows" parent="shibboleth.CommaDelimStringArray" c:_0="%{idp.authn.RemoteUser.c14n.flows:}" />
+    
     <bean id="PopulateSubjectCanonicalizationContext"
         class="net.shibboleth.idp.authn.impl.PopulateSubjectCanonicalizationContext" scope="prototype"
-        p:availableFlows-ref="shibboleth.PostLoginSubjectCanonicalizationFlows" />
+        p:availableFlows-ref="shibboleth.PostLoginC14NFlowDescriptorManager"
+        p:usableFlows-ref="UsableC14NFlows" />
 
     <!-- Audit logging beans. -->
 
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/remoteuser-internal-authn-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/remoteuser-internal-authn-beans.xml
index d07b23ca5..4ae8317f3 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/remoteuser-internal-authn-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/remoteuser-internal-authn-beans.xml
@@ -59,9 +59,12 @@
         p:populateAuditContextAction="#{%{idp.authn.RemoteUserInternal.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('shibboleth.authn.RemoteUserInternal.PopulateAuditContext') : null}"
         p:writeAuditLogAction="#{%{idp.authn.RemoteUserInternal.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('WriteAuthnAuditLog') : null}" />
 
+    <bean id="UsableC14NFlows" parent="shibboleth.CommaDelimStringArray" c:_0="%{idp.authn.RemoteUserInternal.c14n.flows:}" />
+    
     <bean id="PopulateSubjectCanonicalizationContext"
         class="net.shibboleth.idp.authn.impl.PopulateSubjectCanonicalizationContext" scope="prototype"
-        p:availableFlows-ref="shibboleth.PostLoginSubjectCanonicalizationFlows" />
+        p:availableFlows-ref="shibboleth.PostLoginC14NFlowDescriptorManager"
+        p:usableFlows-ref="UsableC14NFlows" />
 
     <!-- Audit logging beans. -->
 
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/saml-authn-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/saml-authn-beans.xml
index b0dd5da83..8a1a164ee 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/saml-authn-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/saml-authn-beans.xml
@@ -456,8 +456,11 @@
         p:attributeExtractionStrategy="#{getObject('shibboleth.authn.SAML.attributeExtractionStrategy')}"
         p:loggedAttributeId="%{idp.authn.SAML.loggedAttributeId:}" />
 
+    <bean id="UsableC14NFlows" parent="shibboleth.CommaDelimStringArray" c:_0="%{idp.authn.SAML.c14n.flows:}" />
+    
     <bean id="PopulateSubjectCanonicalizationContext"
         class="net.shibboleth.idp.authn.impl.PopulateSubjectCanonicalizationContext" scope="prototype"
-        p:availableFlows-ref="shibboleth.PostLoginSubjectCanonicalizationFlows" />
+        p:availableFlows-ref="shibboleth.PostLoginC14NFlowDescriptorManager"
+        p:usableFlows-ref="UsableC14NFlows" />
 
 </beans>
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/spnego-authn-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/spnego-authn-beans.xml
index c6d9e6273..4cafa1ba0 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/spnego-authn-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/spnego-authn-beans.xml
@@ -54,9 +54,12 @@
         p:cookieManager-ref="shibboleth.UserPrefsCookieManager"
         p:cookieName="%{idp.authn.SPNEGO.cookieName:_idp_spnego_autologin}" />
 
+    <bean id="UsableC14NFlows" parent="shibboleth.CommaDelimStringArray" c:_0="%{idp.authn.SPNEGO.c14n.flows:}" />
+    
     <bean id="PopulateSubjectCanonicalizationContext"
         class="net.shibboleth.idp.authn.impl.PopulateSubjectCanonicalizationContext" scope="prototype"
-        p:availableFlows-ref="shibboleth.PostLoginSubjectCanonicalizationFlows" />
+        p:availableFlows-ref="shibboleth.PostLoginC14NFlowDescriptorManager"
+        p:usableFlows-ref="UsableC14NFlows" />
 
     <!-- Audit logging beans. -->
 
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-authn-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-authn-beans.xml
index b901c8917..117bcfcd4 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-authn-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-authn-beans.xml
@@ -31,9 +31,12 @@
         p:populateAuditContextAction="#{%{idp.authn.X509.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('shibboleth.authn.X509.PopulateAuditContext') : null}"
         p:writeAuditLogAction="#{%{idp.authn.X509.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('WriteAuthnAuditLog') : null}" />
 
+    <bean id="UsableC14NFlows" parent="shibboleth.CommaDelimStringArray" c:_0="%{idp.authn.X509.c14n.flows:}" />
+    
     <bean id="PopulateSubjectCanonicalizationContext"
         class="net.shibboleth.idp.authn.impl.PopulateSubjectCanonicalizationContext" scope="prototype"
-        p:availableFlows-ref="shibboleth.PostLoginSubjectCanonicalizationFlows" />
+        p:availableFlows-ref="shibboleth.PostLoginC14NFlowDescriptorManager"
+        p:usableFlows-ref="UsableC14NFlows" />
 
     <!-- Audit logging beans. -->
 
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-internal-authn-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-internal-authn-beans.xml
index 2922e0142..c69c23477 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-internal-authn-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-internal-authn-beans.xml
@@ -32,9 +32,12 @@
         </property>    
     </bean>
 
+    <bean id="UsableC14NFlows" parent="shibboleth.CommaDelimStringArray" c:_0="%{idp.authn.X509Internal.c14n.flows:}" />
+    
     <bean id="PopulateSubjectCanonicalizationContext"
         class="net.shibboleth.idp.authn.impl.PopulateSubjectCanonicalizationContext" scope="prototype"
-        p:availableFlows-ref="shibboleth.PostLoginSubjectCanonicalizationFlows" />
+        p:availableFlows-ref="shibboleth.PostLoginC14NFlowDescriptorManager"
+        p:usableFlows-ref="UsableC14NFlows" />
 
     <!-- Audit logging beans. -->
 
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/authn/authn.properties b/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/authn/authn.properties
index 143f526da..3d412208c 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/authn/authn.properties
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/authn/authn.properties
@@ -78,6 +78,8 @@
 # Default error message handling
 #idp.authn.Password.errorMessageFunction = DefaultPasswordErrorFunction
 #idp.authn.Password.genericMessageID = authn
+# Optionally specify list of c14n beans to apply
+#idp.authn.Password.c14n.flows =
 # Unset if using customized Principals per validator
 #idp.authn.Password.addDefaultPrincipals = true
 # The Principal collection below is the typical default if not otherwise noted.
@@ -106,6 +108,8 @@
 #idp.authn.External.nonBrowserSupported = false
 #idp.authn.External.matchExpression = 
 # Unset if you plan to return full Java Subject from external source
+# Optionally specify list of c14n beans to apply
+#idp.authn.External.c14n.flows =
 #idp.authn.External.addDefaultPrincipals = true
 # Servlet context-relative path to wherever your implementation lives
 idp.authn.External.externalAuthnPath = contextRelative:external.jsp
@@ -115,8 +119,7 @@ idp.authn.External.externalAuthnPath = contextRelative:external.jsp
 #idp.authn.RemoteUser.order = 1000
 #idp.authn.RemoteUser.nonBrowserSupported = false
 #idp.authn.RemoteUser.matchExpression = 
-# Unset in most cases only if using the authnMethodHeader or
-# subjectAttribute settings
+# Unset in most cases only if using the authnMethodHeader or subjectAttribute settings
 #idp.authn.RemoteUser.addDefaultPrincipals = true
 #idp.authn.RemoteUser.checkRemoteUser = true
 # Comma-delimited lists of attributes or headers to pull from
@@ -126,6 +129,8 @@ idp.authn.External.externalAuthnPath = contextRelative:external.jsp
 #idp.authn.RemoteUser.subjectAttribute =
 #idp.authn.RemoteUser.authnMethodHeader =
 #idp.authn.RemoteUser.authnAuthorityHeader =
+# Optionally specify list of c14n beans to apply
+#idp.authn.RemoteUser.c14n.flows =
 
 #### RemoteUserInternal ####
 
@@ -144,6 +149,8 @@ idp.authn.External.externalAuthnPath = contextRelative:external.jsp
 #idp.authn.RemoteUserInternal.matchExpression = 
 #idp.authn.RemoteUserInternal.allowedUsernames = 
 #idp.authn.RemoteUserInternal.deniedUsernames = 
+# Optionally specify list of c14n beans to apply
+#idp.authn.RemoteUserInternal.c14n.flows =
 
 #### SPNEGO ####
 
@@ -152,6 +159,8 @@ idp.authn.External.externalAuthnPath = contextRelative:external.jsp
 #idp.authn.SPNEGO.enforceRun = false
 #idp.authn.SPNEGO.refreshKrbConfig = false
 #idp.authn.SPNEGO.matchExpression = 
+# Optionally specify list of c14n beans to apply
+#idp.authn.SPNEGO.c14n.flows =
 idp.authn.SPNEGO.supportedPrincipals = \
     saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos, \
     saml1/urn:ietf:rfc:1510
@@ -163,6 +172,8 @@ idp.authn.SPNEGO.supportedPrincipals = \
 #idp.authn.X509.saveCertificateToCredentialSet = true
 # Servlet context-relative path to wherever your implementation lives
 #idp.authn.X509.externalAuthnPath = contextRelative:x509-prompt.jsp
+# Optionally specify list of c14n beans to apply
+#idp.authn.X509.c14n.flows =
 idp.authn.X509.supportedPrincipals = \
     saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:X509, \
     saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient, \
@@ -173,6 +184,8 @@ idp.authn.X509.supportedPrincipals = \
 #idp.authn.X509Internal.order = 1000
 #idp.authn.X509Internal.nonBrowserSupported = false
 #idp.authn.X509Internal.saveCertificateToCredentialSet = true
+# Optionally specify list of c14n beans to apply
+#idp.authn.X509Internal.c14n.flows =
 idp.authn.X509Internal.supportedPrincipals = \
     saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:X509, \
     saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient, \
@@ -184,6 +197,8 @@ idp.authn.X509Internal.supportedPrincipals = \
 #idp.authn.IPAddress.passiveAuthenticationSupported = true
 #idp.authn.IPAddress.lifetime = PT60S
 #idp.authn.IPAddress.inactivityTimeout = PT60S
+# Optionally specify list of c14n beans to apply
+#idp.authn.IPAddress.c14n.flows =
 idp.authn.IPAddress.supportedPrincipals = \
    saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol
 
@@ -191,6 +206,8 @@ idp.authn.IPAddress.supportedPrincipals = \
 
 #idp.authn.Function.order = 1000
 #idp.authn.Function.passiveAuthenticationSupported = true
+# Optionally specify list of c14n beans to apply
+#idp.authn.Function.c14n.flows =
 # Unset if you plan to return full Java Subject from function
 #idp.authn.Function.addDefaultPrincipals = true
 
@@ -207,6 +224,8 @@ idp.authn.IPAddress.supportedPrincipals = \
 #   Fall through to discovery via discoveryRequired property
 #idp.authn.SAML.proxyEntityID = https://idp.example.org/idp/shibboleth
 #idp.authn.SAML.discoveryRequired = true
+# Optionally specify list of c14n beans to apply
+#idp.authn.SAML.c14n.flows =
 # Generally left false with bidirectional mappings in
 # conf/authn/authn-comparison.xml across the proxy boundary.
 # Adjust as needed to reflect IdP's capabilities/support.
@@ -224,6 +243,8 @@ idp.authn.IPAddress.supportedPrincipals = \
 #idp.authn.MFA.validateLoginTransitions = true
 # Defaults to set AuthnInstant based on oldest component result
 #idp.authn.MFA.useLatestTimestamp = false
+# Optionally specify list of c14n beans to apply
+#idp.authn.MFA.c14n.flows =
 # The list below almost certainly requires changes, and should generally be the
 # union of any of the separate factors you combine in your particular MFA flow
 # rules. The example corresponds to the example in mfa-authn-config.xml that

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


More information about the commits mailing list