[java-idp-plugin-webauthn] 11/11: JWEBAUTHN-27 - Add basic authenticator policy

Phil Smart philip.smart at jisc.ac.uk
Fri Oct 18 17:13:36 UTC 2024


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

philsmart pushed a commit to branch main
in repository java-idp-plugin-webauthn.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-webauthn.git;a=commit;h=481a82fd58f11122e224d7d11d399c3427a3c1b2

commit 481a82fd58f11122e224d7d11d399c3427a3c1b2
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Oct 18 18:11:17 2024 +0100

    JWEBAUTHN-27 - Add basic authenticator policy
    
     - Add policy and inspector rule classes that allow scripted functions
    to be used.
     - Split config into registration, authentication, and management XML
    files
     - Add default policy and inspector lists into user config
     - Checkstyle fixes
    
     https://shibboleth.atlassian.net/browse/JWEBAUTHN-27
---
 .../policy/AuthenticatorCapabilitiesInspector.java |  2 +-
 .../webauthn/admin/policy/AuthenticatorPolicy.java |  3 +-
 .../authn/webauthn/admin/policy/package-info.java  | 18 ++++++
 .../authn/webauthn/authn/AssertionResult.java      |  2 +-
 .../webauthn/context/InlineEnrolmentContext.java   |  2 +
 .../context/WebAuthnAuthenticationContext.java     |  2 +
 .../context/WebAuthnRegistrationContext.java       |  2 +
 .../authn/webauthn/metadata/AaguidEntry.java       |  8 +--
 .../authn/webauthn/metadata/package-info.java      | 18 ++++++
 .../plugin/authn/webauthn/policy/package-info.java | 18 ++++++
 .../admin/impl/CheckAuthenticatorPolicy.java       |  3 +-
 .../admin/impl/PopulateInlineEnrolmentContext.java | 11 ++--
 ...AbstractAuthenticatorCapabilitiesInspector.java |  6 +-
 .../impl/AbstractAuthenticatorPolicyRule.java      |  7 ++-
 .../policy/impl/AllowlistAuthenticatorPolicy.java  |  3 +-
 .../impl/AuthenticatorCapabilitiesPolicyRule.java  |  6 +-
 ...ChainingAuthenticatorCapabilitiesInspector.java |  2 +-
 .../impl/ChainingAuthenticatorPolicyRule.java      |  3 +-
 .../impl/FunctionalAuthenticatorInspectorRule.java | 69 +++++++++++++++++++++
 .../impl/FunctionalAuthenticatorPolicyRule.java    | 65 ++++++++++++++++++++
 .../SecondFactorOnlyAuthenticatorInspector.java    |  5 +-
 .../webauthn/admin/policy/impl/package-info.java   | 18 ++++++
 .../authn/webauthn/impl/CheckCredentialPolicy.java |  4 +-
 .../metadata/impl/FidoMetadataServiceFactory.java  |  2 +-
 .../impl/PasskeyAaguidMetadataFactory.java         |  5 +-
 .../impl/PasskeyAaguidMetadataService.java         |  3 +-
 .../policy/impl/AbstractCredentialPolicyRule.java  |  2 +-
 .../impl/FunctionalCredentialPolicyRule.java       | 65 ++++++++++++++++++++
 .../policy/impl/FunctionalRuleContext.java         | 70 ++++++++++++++++++++++
 .../authn/webauthn/policy/impl/package-info.java   | 18 ++++++
 .../webauthn-management-beans.xml                  |  1 +
 .../webauthn-registration-beans.xml                | 39 ++++--------
 .../idp/flows/authn/WebAuthn/webauthn-beans.xml    | 17 +++---
 .../authn/webauthn/conf/authn/webauthn-config.xml  |  9 +++
 ...n-config.xml => webauthn-management-config.xml} |  1 -
 .../conf/authn/webauthn-registration-config.xml    | 32 ++++++++++
 .../conf/authn/webauthn-registration.properties    | 19 +++---
 .../authn/webauthn/conf/authn/webauthn.properties  | 11 +---
 .../idp/plugin/authn/webauthn/module.properties    | 50 +++++++++-------
 .../plugin/authn/webauthn/views/webauthn-authn.vm  |  6 +-
 .../plugin/authn/webauthn/views/webauthn-end.vm    |  4 +-
 .../webauthn/flow/TestAdminManagementFlow.java     |  2 +-
 .../authn/webauthn/flow/TestPasswordlessFlow.java  |  2 +-
 .../authn/webauthn/flow/TestRegistrationFlow.java  |  2 +-
 .../authn/webauthn/flow/TestSecondFactorFlow.java  |  2 +-
 .../authn/webauthn/flow/TestUsernameslessFlow.java |  2 +-
 .../flow/TestUsernameslessFlowWithPolicy.java      |  3 +-
 ...est-beans.xml => test-beans-authentication.xml} | 16 ++++-
 .../{test-beans.xml => test-beans-management.xml}  |  2 -
 .../authn/webauthn/test-beans-registration.xml     | 47 +++++++++++++++
 50 files changed, 585 insertions(+), 124 deletions(-)

diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/AuthenticatorCapabilitiesInspector.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/AuthenticatorCapabilitiesInspector.java
index b1b1350..a736bd5 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/AuthenticatorCapabilitiesInspector.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/AuthenticatorCapabilitiesInspector.java
@@ -42,6 +42,6 @@ public interface AuthenticatorCapabilitiesInspector extends IdentifiedComponent
      * @return a map of capabilities/properties/labels/tags relating to the authenticator, could be empty.
      */
     @Nonnull @NotLive @Unmodifiable 
-    Map<String, String> inspect(@Nullable final AAGUID aaguid, @Nullable final ProfileRequestContext prc);
+    Map<String, String> inspect(@Nullable final AAGUID aaguid, @Nonnull final ProfileRequestContext prc);
 
 }
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/AuthenticatorPolicy.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/AuthenticatorPolicy.java
index 0e280cd..40a11c9 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/AuthenticatorPolicy.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/AuthenticatorPolicy.java
@@ -15,7 +15,6 @@
 package net.shibboleth.idp.plugin.authn.webauthn.admin.policy;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 
 import org.opensaml.profile.context.ProfileRequestContext;
 
@@ -60,6 +59,6 @@ public interface AuthenticatorPolicy extends IdentifiedComponent{
      * @return the authenticator policy outcome: ACCEPT if allowed, REJECT if rejected, and IGNORE if the
      *      policy is to be ignored (e.g. the policy is not active)
      */
-    AuthenticatorPolicyOutcome evaluate(@Nonnull final AAGUID aaguid, @Nullable final ProfileRequestContext prc);
+    AuthenticatorPolicyOutcome evaluate(@Nonnull final AAGUID aaguid, @Nonnull final ProfileRequestContext prc);
 
 }
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/package-info.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/package-info.java
new file mode 100644
index 0000000..24982f8
--- /dev/null
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+/**
+ *  This package contains the APIs for registration policies.
+ */
+package net.shibboleth.idp.plugin.authn.webauthn.admin.policy;
\ No newline at end of file
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/authn/AssertionResult.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/authn/AssertionResult.java
index 2258b69..64a03c4 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/authn/AssertionResult.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/authn/AssertionResult.java
@@ -23,7 +23,7 @@ import net.shibboleth.shared.annotation.constraint.NotEmpty;
  * The result of calling {@link WebAuthnAuthenticationClient#validateAuthenticatorAssertionResponse(String, byte[], 
  * com.yubico.webauthn.data.PublicKeyCredentialRequestOptions, com.yubico.webauthn.data.PublicKeyCredential)}.
  * 
- * <p>Note, an instantiatable version of Yuico's AssertionResult.</p>
+ * <p>Note, an instantiatable version of Yubico's AssertionResult.</p>
  */
 public final class AssertionResult {
     
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/InlineEnrolmentContext.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/InlineEnrolmentContext.java
index c4503be..bd7ddac 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/InlineEnrolmentContext.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/InlineEnrolmentContext.java
@@ -32,6 +32,8 @@ public class InlineEnrolmentContext extends BaseContext {
      * Set the URL to redirect the user to after the flow has ended.
      * 
      * @param url The sso Url to set.
+     * 
+     * @return this context
      */
     public InlineEnrolmentContext setSsoUrl(@Nullable final URL url) {
         ssoUrl = url;
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnAuthenticationContext.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnAuthenticationContext.java
index 67a6a07..875c928 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnAuthenticationContext.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnAuthenticationContext.java
@@ -190,6 +190,8 @@ public final class WebAuthnAuthenticationContext extends BaseWebAuthnContext {
      * Set the credential used by the authenticator for this authentication.
      * 
      * @param credential The authentication credential to set.
+     * 
+     * @return this context
      */
     @Nonnull public WebAuthnAuthenticationContext setAuthenticationCredential(
             @Nullable final CredentialRegistration credential) {
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationContext.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationContext.java
index a2e9765..b899908 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationContext.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationContext.java
@@ -321,6 +321,8 @@ public final class WebAuthnRegistrationContext extends BaseWebAuthnContext {
      * Set the capabilities map of the authenticator. 
      * 
      * @param capabilities the map of capabilities to set
+     * 
+     * @return this context
      */
     @Nonnull public BaseWebAuthnContext setAuthenticatorCapabilities(@Nullable final Map<String, String> capabilities) {
         authenticatorCapabilities = capabilities;
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/AaguidEntry.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/AaguidEntry.java
index f92e456..a64f374 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/AaguidEntry.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/AaguidEntry.java
@@ -36,13 +36,13 @@ public class AaguidEntry {
     @JsonProperty("name")
     private String name;
     /**
-     *  The base64 SVG data encoded dark icon for this provider
+     *  The base64 SVG data encoded dark icon for this provider.
      */
     @JsonProperty("icon_dark")
     @JsonPropertyDescription("")
     private String iconDark;
     /**
-     *  The base64 SVG data encoded light icon for this provider
+     *  The base64 SVG data encoded light icon for this provider.
      */
     @JsonProperty("icon_light")
     @JsonPropertyDescription("")
@@ -51,7 +51,7 @@ public class AaguidEntry {
     /**
      * Get the name of the authenticator/provider.
      * 
-     * return the name
+     * @return the name
      */
     @JsonProperty("name")
     public String getName() {
@@ -61,7 +61,7 @@ public class AaguidEntry {
     /**
      * Set the name of the authenticator/provider.
      * 
-     * @param name the name of the authenticator/provider
+     * @param nameIn the name of the authenticator/provider
      */
     @JsonProperty("name")
     public void setName(final String nameIn) {
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/package-info.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/package-info.java
new file mode 100644
index 0000000..2c8884f
--- /dev/null
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+/**
+ *  This package contains the APIs for handling FIDO metadata.
+ */
+package net.shibboleth.idp.plugin.authn.webauthn.metadata;
\ No newline at end of file
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/package-info.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/package-info.java
new file mode 100644
index 0000000..2580e2f
--- /dev/null
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+/**
+ *  This package contains the APIs for authentication policies.
+ */
+package net.shibboleth.idp.plugin.authn.webauthn.policy;
\ No newline at end of file
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CheckAuthenticatorPolicy.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CheckAuthenticatorPolicy.java
index 0d2ae87..b893eec 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CheckAuthenticatorPolicy.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CheckAuthenticatorPolicy.java
@@ -134,7 +134,8 @@ public class CheckAuthenticatorPolicy extends AbstractWebAuthnAction<WebAuthnReg
             return;
         } else {
             if (log.isDebugEnabled()) {
-                log.debug("{} Authenticator '{}' accepted", getLogPrefix(), authenticatorAttestationGUID.asGuidString());
+                log.debug("{} Authenticator '{}' accepted", getLogPrefix(), 
+                        authenticatorAttestationGUID.asGuidString());
             }
         }
              
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/PopulateInlineEnrolmentContext.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/PopulateInlineEnrolmentContext.java
index 402d04a..bece83f 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/PopulateInlineEnrolmentContext.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/PopulateInlineEnrolmentContext.java
@@ -30,6 +30,7 @@ import com.google.common.net.HttpHeaders;
 import jakarta.servlet.http.HttpServletRequest;
 import net.shibboleth.idp.plugin.authn.webauthn.context.InlineEnrolmentContext;
 import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.logic.PredicateSupport;
 import net.shibboleth.shared.primitive.LoggerFactory;
@@ -38,14 +39,14 @@ import net.shibboleth.shared.primitive.LoggerFactory;
  * Populate the {@link InlineEnrolmentContext} from the HTTP referer header in the
  * HTTP request iff the registration query parameter contains 'inline'.  
  */
-public class PopulateInlineEnrolmentContext extends AbstractProfileAction {
+public class PopulateInlineEnrolmentContext extends AbstractProfileAction {    
+    
+    /** The name of the query parameter that contains a signal that the request was for inline enrolment.*/
+    @Nonnull @NotEmpty private static final String REG_QUERY_PARAM = "reg";
     
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(PopulateInlineEnrolmentContext.class);
     
-    /** The name of the query parameter that contains a signal that the request was for inline enrolment.*/
-    private static final String REG_QUERY_PARAM = "reg";
-    
     /** Is inline enrolment enabled? Enabled by default. */
     private Predicate<ProfileRequestContext> enabled;
     
@@ -95,7 +96,7 @@ public class PopulateInlineEnrolmentContext extends AbstractProfileAction {
     /** {@inheritDoc} */
     @Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
         
-        if (enabled.test(profileRequestContext) == false) {
+        if (!enabled.test(profileRequestContext)) {
             return;
         }
         
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AbstractAuthenticatorCapabilitiesInspector.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AbstractAuthenticatorCapabilitiesInspector.java
index 27c2627..830df91 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AbstractAuthenticatorCapabilitiesInspector.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AbstractAuthenticatorCapabilitiesInspector.java
@@ -79,7 +79,7 @@ public abstract class AbstractAuthenticatorCapabilitiesInspector extends Abstrac
     /**
      * Set the FIDO Metadata service.
      * 
-     * @param service the FIDO Metadata service.
+     * @param trustSource the FIDO Metadata service.
      */
     public void setFidoMetadataService(@Nullable final FidoMetadataService trustSource) {
         checkSetterPreconditions();
@@ -97,7 +97,7 @@ public abstract class AbstractAuthenticatorCapabilitiesInspector extends Abstrac
     
     /** {@inheritDoc} */
     @Override
-    public Map<String, String> inspect(@Nullable final AAGUID aaguid, @Nullable final ProfileRequestContext prc) {
+    public Map<String, String> inspect(@Nullable final AAGUID aaguid, @Nonnull final ProfileRequestContext prc) {
         if (!activationCondition.test(aaguid, prc)) {
             //not active for this request
             log.trace("AuthenticatorCapabilitiesInspector '{}' not active for this request", getId());
@@ -115,7 +115,7 @@ public abstract class AbstractAuthenticatorCapabilitiesInspector extends Abstrac
      * @return a map of capabilities.
      */
     @Nonnull 
-    protected abstract Map<String, String> doInspect(@Nullable AAGUID aaguid, @Nullable ProfileRequestContext prc);
+    protected abstract Map<String, String> doInspect(@Nullable AAGUID aaguid, @Nonnull ProfileRequestContext prc);
     
     
 }
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AbstractAuthenticatorPolicyRule.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AbstractAuthenticatorPolicyRule.java
index 96cc82a..0e50e1e 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AbstractAuthenticatorPolicyRule.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AbstractAuthenticatorPolicyRule.java
@@ -80,7 +80,7 @@ public abstract class AbstractAuthenticatorPolicyRule extends AbstractIdentifiab
     /**
      * Set the attestation trust source.
      * 
-     * @param service the attestation trust source.
+     * @param trustSource the attestation trust source.
      */
     public void setFidoMetadataService(@Nullable final FidoMetadataService trustSource) {
         checkSetterPreconditions();
@@ -98,7 +98,8 @@ public abstract class AbstractAuthenticatorPolicyRule extends AbstractIdentifiab
     
     /** {@inheritDoc} */
     @Override
-    public AuthenticatorPolicyOutcome evaluate(@Nullable final AAGUID aaguid, @Nullable final ProfileRequestContext prc) {
+    public AuthenticatorPolicyOutcome evaluate(@Nullable final AAGUID aaguid, 
+            @Nonnull final ProfileRequestContext prc) {
         if (!activationCondition.test(aaguid, prc)) {
             //not active for this request
             log.trace("AuthenticatorPolicy rule '{}' not active for this request", getId());
@@ -118,6 +119,6 @@ public abstract class AbstractAuthenticatorPolicyRule extends AbstractIdentifiab
      * 
      * @return true if the policy allows the authenticator, false otherwise.
      */
-    protected abstract AuthenticatorPolicyOutcome doAccept(@Nonnull AAGUID aaguid, @Nullable ProfileRequestContext prc);
+    protected abstract AuthenticatorPolicyOutcome doAccept(@Nonnull AAGUID aaguid, @Nonnull ProfileRequestContext prc);
 
 }
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AllowlistAuthenticatorPolicy.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AllowlistAuthenticatorPolicy.java
index 0f556d1..7084732 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AllowlistAuthenticatorPolicy.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AllowlistAuthenticatorPolicy.java
@@ -70,7 +70,8 @@ public class AllowlistAuthenticatorPolicy extends AbstractAuthenticatorPolicyRul
 
     /** {@inheritDoc} */
     @Override
-    public AuthenticatorPolicyOutcome doAccept(@Nonnull final AAGUID aaguid, @Nullable final ProfileRequestContext prc) {        
+    public AuthenticatorPolicyOutcome doAccept(@Nonnull final AAGUID aaguid, 
+            @Nullable final ProfileRequestContext prc) {        
         return AuthenticatorPolicyOutcome.of(allowedAuthenticators.contains(aaguid));
     }
 
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AuthenticatorCapabilitiesPolicyRule.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AuthenticatorCapabilitiesPolicyRule.java
index b2907fc..8cb80ac 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AuthenticatorCapabilitiesPolicyRule.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AuthenticatorCapabilitiesPolicyRule.java
@@ -52,7 +52,7 @@ public class AuthenticatorCapabilitiesPolicyRule extends AbstractAuthenticatorPo
     }
     
     /**
-     * Set the predicate that determines if this authenticators capabilities should be accepted or rejected
+     * Set the predicate that determines if this authenticators capabilities should be accepted or rejected.
      * 
      * @param predicate the predicate which determines if this authenticator, based on its metadata, should be 
      *      accepted.
@@ -71,8 +71,8 @@ public class AuthenticatorCapabilitiesPolicyRule extends AbstractAuthenticatorPo
         checkComponentActive();
         final FidoMetadataService metadata = getFidoMetadataService();
         if (metadata == null) {
-            log.warn("{} AuthenticatorCapabilities Policy Rule can not access attestation trust source, is metadata suported enabled?"
-                    + " rejecting", 
+            log.warn("{} AuthenticatorCapabilities Policy Rule can not access attestation trust source, is "
+                    + "metadata suported enabled? rejecting", 
                     getId());
             return AuthenticatorPolicyOutcome.REJECT;
         }
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/ChainingAuthenticatorCapabilitiesInspector.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/ChainingAuthenticatorCapabilitiesInspector.java
index e5269df..9e638e2 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/ChainingAuthenticatorCapabilitiesInspector.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/ChainingAuthenticatorCapabilitiesInspector.java
@@ -70,7 +70,7 @@ public class ChainingAuthenticatorCapabilitiesInspector extends AbstractAuthenti
 
     /** {@inheritDoc} */
     @Override
-    protected Map<String,String> doInspect(@Nullable final AAGUID aaguid, @Nullable final ProfileRequestContext prc) {
+    protected Map<String,String> doInspect(@Nullable final AAGUID aaguid, @Nonnull final ProfileRequestContext prc) {
         
         final Map<String, String> allOutcomes = new HashMap<>();
         for (final AuthenticatorCapabilitiesInspector inspector : authenticatorInspectorChain) {
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/ChainingAuthenticatorPolicyRule.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/ChainingAuthenticatorPolicyRule.java
index f941442..df94d4a 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/ChainingAuthenticatorPolicyRule.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/ChainingAuthenticatorPolicyRule.java
@@ -70,7 +70,8 @@ public class ChainingAuthenticatorPolicyRule extends AbstractAuthenticatorPolicy
 
     /** {@inheritDoc} */
     @Override
-    protected AuthenticatorPolicyOutcome doAccept(@Nonnull final AAGUID aaguid, @Nullable final ProfileRequestContext prc) {
+    protected AuthenticatorPolicyOutcome doAccept(@Nonnull final AAGUID aaguid, 
+            @Nonnull final ProfileRequestContext prc) {
         
         for (final AuthenticatorPolicy policy : authenticatorPolicyChain) {
             if (log.isTraceEnabled()) {
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/FunctionalAuthenticatorInspectorRule.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/FunctionalAuthenticatorInspectorRule.java
new file mode 100644
index 0000000..6609919
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/FunctionalAuthenticatorInspectorRule.java
@@ -0,0 +1,69 @@
+/*
+ * 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.authn.webauthn.admin.policy.impl;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+
+import com.yubico.fido.metadata.AAGUID;
+
+import net.shibboleth.idp.plugin.authn.webauthn.admin.policy.AuthenticatorCapabilitiesInspector;
+import net.shibboleth.idp.plugin.authn.webauthn.policy.impl.FunctionalRuleContext;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * A {@link AuthenticatorCapabilitiesInspector} that uses a {@link BiFunction} to inspect the authenticator that 
+ * created the credential registration. 
+ */
+public class FunctionalAuthenticatorInspectorRule extends AbstractAuthenticatorCapabilitiesInspector {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(FunctionalAuthenticatorInspectorRule.class);
+    
+    /** The function to run. By default this returns an empty map.*/
+    @Nonnull private BiFunction<AAGUID, FunctionalRuleContext, Map<String, String>> inspector;
+    
+    /** Constructor.*/
+    public FunctionalAuthenticatorInspectorRule() {
+        inspector = (cred, prc) -> CollectionSupport.emptyMap();
+    }
+    
+    /**
+     * Set the inspector function rule to run. 
+     * 
+     * @param function the inspector to set.
+     */
+    public void setInspector(@Nonnull 
+            final BiFunction<AAGUID, FunctionalRuleContext, Map<String, String>> function) {
+        checkSetterPreconditions();
+        inspector = Constraint.isNotNull(function, "Inspector function can not be null");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull protected Map<String, String> doInspect(final AAGUID aaguid, final ProfileRequestContext prc) {
+        log.debug("Running AuthenticatorInspector rule function '{}'", this.getId());
+        final Map<String, String> capabilities = inspector.apply(aaguid, 
+                new FunctionalRuleContext(prc, getFidoMetadataService()));
+        return capabilities == null ? CollectionSupport.emptyMap() : capabilities;
+    }
+
+}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/FunctionalAuthenticatorPolicyRule.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/FunctionalAuthenticatorPolicyRule.java
new file mode 100644
index 0000000..f6e3c13
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/FunctionalAuthenticatorPolicyRule.java
@@ -0,0 +1,65 @@
+/*
+ * 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.authn.webauthn.admin.policy.impl;
+import java.util.function.BiFunction;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+
+import com.yubico.fido.metadata.AAGUID;
+
+import net.shibboleth.idp.plugin.authn.webauthn.admin.policy.AuthenticatorPolicy;
+import net.shibboleth.idp.plugin.authn.webauthn.policy.impl.FunctionalRuleContext;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * A {@link AuthenticatorPolicy} that evaluates a {@link BiFunction} to determine if the authenticator that created
+ * the credential registration should be accepted or rejected.
+ */
+public class FunctionalAuthenticatorPolicyRule extends AbstractAuthenticatorPolicyRule {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(FunctionalAuthenticatorPolicyRule.class);
+    
+    /** The function to run. By default this returns {@link AuthenticatorPolicyOutcome#IGNORE}.*/
+    @Nonnull private BiFunction<AAGUID, FunctionalRuleContext, AuthenticatorPolicyOutcome> rule;
+    
+    /** Constructor.*/
+    public FunctionalAuthenticatorPolicyRule() {
+        rule = (cred, rc) -> AuthenticatorPolicyOutcome.IGNORE;
+    }
+    
+    /**
+     * Set the policy function rule to run. 
+     * 
+     * @param function the rule to set.
+     */
+    public void setRule(@Nonnull 
+            final BiFunction<AAGUID, FunctionalRuleContext, AuthenticatorPolicyOutcome> function) {
+        checkSetterPreconditions();
+        rule = Constraint.isNotNull(function, "Rule function can not be null");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected AuthenticatorPolicyOutcome doAccept(final AAGUID aaguid, final ProfileRequestContext prc) {
+        log.debug("Running AuthenticatorPolicy rule function '{}'", this.getId());
+        return rule.apply(aaguid, new FunctionalRuleContext(prc,getFidoMetadataService()));
+    }
+
+}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/SecondFactorOnlyAuthenticatorInspector.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/SecondFactorOnlyAuthenticatorInspector.java
index 610a749..7d09a4b 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/SecondFactorOnlyAuthenticatorInspector.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/SecondFactorOnlyAuthenticatorInspector.java
@@ -33,6 +33,7 @@ import net.shibboleth.shared.annotation.constraint.NotLive;
 import net.shibboleth.shared.annotation.constraint.Unmodifiable;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
 
 /**
  * Tag credentials created by authenticators that should only be used for second-factor authentication.
@@ -61,7 +62,9 @@ public class SecondFactorOnlyAuthenticatorInspector extends AbstractAuthenticato
     public void setSecondFactorOnlyAuthenticators(final Set<String> allowed) {
         checkSetterPreconditions();
         if (allowed != null) {
-            secondFactorOnlyAuthenticators = allowed.stream().map(strAAGUID -> {
+            secondFactorOnlyAuthenticators = allowed.stream().filter(strAAGUID -> 
+            StringSupport.trimOrNull(strAAGUID)!=null)
+                    .map(strAAGUID -> {
                 final var aaguidBytes = AuthenticatorSupport.parse(strAAGUID);
                 if (aaguidBytes != null) {
                     return new AAGUID(aaguidBytes);
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/package-info.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/package-info.java
new file mode 100644
index 0000000..77d53c7
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+/**
+ *  This package has registration policy implementations.
+ */
+package net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl;
\ No newline at end of file
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/CheckCredentialPolicy.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/CheckCredentialPolicy.java
index 8303b7e..2d287ec 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/CheckCredentialPolicy.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/CheckCredentialPolicy.java
@@ -155,8 +155,8 @@ public class CheckCredentialPolicy extends AbstractWebAuthnAction<WebAuthnAuthen
             ActionSupport.buildEvent(profileRequestContext, WebAuthnAuthenticationEventIds.CREDENTIAL_POLICY_REJECTION);
             return;
         } else if (outcome == CredentialPolicyOutcome.IGNORE) {
-            log.trace("{} CredentialPolicy '{}' was not active for credential '{}', accepting", getLogPrefix(), localPolicy.getId(),
-                    credential.get().getCredential().getCredentialId().getBase64Url());
+            log.trace("{} CredentialPolicy '{}' was not active for credential '{}', accepting", getLogPrefix(), 
+                    localPolicy.getId(), credential.get().getCredential().getCredentialId().getBase64Url());
             return;
         }
         log.debug("{} CredentialPolicy '{}' accepted credential '{}'", getLogPrefix(), localPolicy.getId(),
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/FidoMetadataServiceFactory.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/FidoMetadataServiceFactory.java
index 879543a..ebb459c 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/FidoMetadataServiceFactory.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/FidoMetadataServiceFactory.java
@@ -320,7 +320,7 @@ public class FidoMetadataServiceFactory extends AbstractIdentifiableInitializabl
     /**
      * Set if signatures are only verified in the blob file from a fresh download.
      * 
-     * @param verifyDownloadOnly The verifyDownloadOnly to set.
+     * @param downloadOnly should we verify only newly downloaded metadata.
      */
     public synchronized void setVerifyDownloadOnly(final boolean downloadOnly) {
         checkSetterPreconditions();
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/PasskeyAaguidMetadataFactory.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/PasskeyAaguidMetadataFactory.java
index 36a3ea7..1a2be58 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/PasskeyAaguidMetadataFactory.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/PasskeyAaguidMetadataFactory.java
@@ -53,7 +53,7 @@ public class PasskeyAaguidMetadataFactory extends AbstractIdentifiableInitializa
     }
     
     /**
-     * Get the passkey Aaguid JSON file
+     * Get the passkey Aaguid JSON file.
      * 
      * @return the passkey Aaguid JSON file.
      */
@@ -69,7 +69,8 @@ public class PasskeyAaguidMetadataFactory extends AbstractIdentifiableInitializa
         try {
             if (localAaguidResource != null) {
                 final Map<String,AaguidEntry> metadataObject = 
-                        metadataObjMapper.readValue(localAaguidResource.getFile(), new TypeReference<HashMap<String, AaguidEntry>>() {});
+                        metadataObjMapper.readValue(localAaguidResource.getFile(), 
+                                new TypeReference<HashMap<String, AaguidEntry>>() {});
                 return new PasskeyAaguidMetadataService(metadataObject);
             } else {
                 throw new FatalBeanException("Can not construct the Passkey AAGUID metadata provider, "
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/PasskeyAaguidMetadataService.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/PasskeyAaguidMetadataService.java
index cb6e400..726f09c 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/PasskeyAaguidMetadataService.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/metadata/impl/PasskeyAaguidMetadataService.java
@@ -32,12 +32,13 @@ import net.shibboleth.shared.logic.Constraint;
  */
 public class PasskeyAaguidMetadataService {
     
+    /** The metadata.*/
     @Nonnull private final Map<String,AaguidEntry> metadata;
 
     /**
      * Constructor.
      *
-     * @param metadata the map of authenticator attestation GUID to entry.
+     * @param mtdata the map of authenticator attestation GUID to entry.
      */
     public PasskeyAaguidMetadataService(@Nonnull final Map<String,AaguidEntry> mtdata) {
         super();
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/AbstractCredentialPolicyRule.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/AbstractCredentialPolicyRule.java
index f6cd2d2..605b34b 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/AbstractCredentialPolicyRule.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/AbstractCredentialPolicyRule.java
@@ -105,7 +105,7 @@ public abstract class AbstractCredentialPolicyRule extends AbstractIdentifiableI
     /**
      * Set the FIDO metadata source.
      * 
-     * @param service the attestation trust source.
+     * @param trustSource the attestation trust source.
      */
     public void setFidoMetadataService(@Nullable final FidoMetadataService trustSource) {
         checkSetterPreconditions();
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/FunctionalCredentialPolicyRule.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/FunctionalCredentialPolicyRule.java
new file mode 100644
index 0000000..2f1786a
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/FunctionalCredentialPolicyRule.java
@@ -0,0 +1,65 @@
+/*
+ * 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.authn.webauthn.policy.impl;
+import java.util.function.BiFunction;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.policy.CredentialPolicy;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRegistration;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * A {@link CredentialPolicy} that evaluates a {@link BiFunction} to determine if a credential should be accepted or
+ * rejected.
+ */
+public class FunctionalCredentialPolicyRule extends AbstractCredentialPolicyRule {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(FunctionalCredentialPolicyRule.class);
+    
+    /** The function to run. By default this returns {@link CredentialPolicyOutcome#IGNORE}.*/
+    @Nonnull private BiFunction<CredentialRegistration, FunctionalRuleContext, CredentialPolicyOutcome> rule;
+    
+    /** Constructor.*/
+    public FunctionalCredentialPolicyRule() {
+        rule = (cred, prc) -> CredentialPolicyOutcome.IGNORE;
+    }
+    
+    /**
+     * Set the policy function to run. 
+     * 
+     * @param function the rule to set.
+     */
+    public void setRule(@Nonnull 
+            final BiFunction<CredentialRegistration, FunctionalRuleContext, CredentialPolicyOutcome> function) {
+        checkSetterPreconditions();
+        rule = Constraint.isNotNull(function, "Rule function can not be null");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected CredentialPolicyOutcome doEvaluate(final CredentialRegistration credential, 
+            final ProfileRequestContext prc, final WebAuthnAuthenticationContext webAuthnContext) {
+        log.debug("Running CredentialPolicy rule function '{}'", this.getId());
+        return rule.apply(credential, new FunctionalRuleContext(prc, getFidoMetadataService()));
+    }
+
+}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/FunctionalRuleContext.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/FunctionalRuleContext.java
new file mode 100644
index 0000000..c895a93
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/FunctionalRuleContext.java
@@ -0,0 +1,70 @@
+/*
+ * 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.authn.webauthn.policy.impl;
+
+import java.util.function.BiFunction;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.yubico.fido.metadata.FidoMetadataService;
+
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * A context class that provides access to the {@link ProfileRequestContext} and {@link FidoMetadataService} from
+ * {@link BiFunction BiFunctions} that implement registration or authentication rules.
+ */
+public class FunctionalRuleContext {
+    
+    /** The stored profile request context.*/
+    @Nonnull private final ProfileRequestContext profileRequestContext;
+    
+    /** The stored FIDO metadata service.*/
+    @Nullable private final FidoMetadataService metadataService;
+
+    /**
+     * Constructor.
+     *
+     * @param prc the stored profile request context
+     * @param metadata the stored metadata service
+     */
+    public FunctionalRuleContext(@Nonnull final ProfileRequestContext prc,
+            @Nullable final FidoMetadataService metadata) {
+        profileRequestContext = Constraint.isNotNull(prc, "The ProfileRequestContext can not be null");
+        metadataService = metadata;
+    }
+    
+    /**
+     * Get the metadata service. 
+     * 
+     * @return the metadata service.
+     */
+    @Nullable public FidoMetadataService getMetadataService() {
+        return metadataService;
+    }
+    
+    /**
+     * Get the profile request context.
+     * 
+     * @return the profile request context.
+     */
+    @Nonnull public ProfileRequestContext getProfileRequestContext() {
+        return profileRequestContext;
+    }
+
+}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/package-info.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/package-info.java
new file mode 100644
index 0000000..8569470
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+/**
+ *  This package has authentication policy implementations.
+ */
+package net.shibboleth.idp.plugin.authn.webauthn.policy.impl;
\ No newline at end of file
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-beans.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-beans.xml
index 2da5559..08dea52 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-beans.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-beans.xml
@@ -133,5 +133,6 @@
         </property>
     </bean>
 
+    <import resource="conditional:%{idp.home}/conf/authn/webauthn-management-config.xml" />
 
 </beans>
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml
index 5de5fb2..cede9ca 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml
@@ -37,6 +37,15 @@
     <bean id="AbstractAuthenticatorPolicyRule" scope="prototype" abstract="true"
         p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.webauthn.DefaultWebAuthnFidoMetadataServiceFactory')}"/>
     
+    <bean id="shibboleth.authn.webauthn.registration.FunctionalAuthenticatorPolicyRule" abstract="true"
+        class=" net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.FunctionalAuthenticatorPolicyRule" 
+        p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.webauthn.DefaultWebAuthnFidoMetadataServiceFactory')}"/>
+        
+    <bean id="shibboleth.authn.webauthn.registration.FunctionalAuthenticatorInspectorRule" abstract="true"
+        class=" net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.FunctionalAuthenticatorInspectorRule" 
+        p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.webauthn.DefaultWebAuthnFidoMetadataServiceFactory')}"/>
+        
+    
     <!-- Flow beans -->
     
     <bean id="PopulateInlineEnrolmentContext" scope="prototype" 
@@ -180,16 +189,7 @@
         
      <bean id="shibboleth.authn.webauthn.registration.ChainedAuthenticatorPolicy" scope="prototype"
         class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.ChainingAuthenticatorPolicyRule"
-        p:authenticatorPolicyChain="#{getObject('%{idp.authn.webauthn.registration.authenticator.policy.chainedlist:shibboleth.authn.webauthn.registration.ChainedAuthenticatorPolicies}')}"/>
-    
-    <util:list id="shibboleth.authn.webauthn.registration.ChainedAuthenticatorPolicies">  
-      
-        <bean id="AllowlistAuthenticatorPolicy" parent="AbstractAuthenticatorPolicyRule" 
-            class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.AllowlistAuthenticatorPolicy"
-            p:allowedAuthenticators="%{idp.authn.webauthn.registration.authenticator.policy.allowedAuthenticators:null}"
-            p:activationCondition="%{idp.authn.webauthn.registration.authenticator.policy.allowedAuthenticators.enabled:true}"/>
-         
-    </util:list>
+        p:authenticatorPolicyChain="#{getObject('%{idp.authn.webauthn.registration.authenticator.policy.chainedlist:shibboleth.authn.webauthn.registration.ChainedAuthenticatorPolicyList}')}"/>
     
     <bean id="AttachAuthenticatorCapabilities" parent="AbstractWebAuthnRegistrationAction" scope="prototype"
         class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.AttachAuthenticatorCapabilities"
@@ -198,21 +198,7 @@
          
     <bean id="shibboleth.authn.webauthn.registration.ChainedAuthenticatorCapabilitiesInspector" scope="prototype"
         class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.ChainingAuthenticatorCapabilitiesInspector"
-        p:authenticatorInspectorChain="#{getObject('%{idp.authn.webauthn.registration.authenticator.inspector.chainedlist:shibboleth.authn.webauthn.registration.ChainedAuthenticatorInspectors}')}"/>
-   
-    
-    <util:list id="shibboleth.authn.webauthn.registration.ChainedAuthenticatorInspectors">  
-      
-        <bean id="shibboleth.authn.webauthn.registration.SecondFactorOnlyInspector" scope="prototype"
-            class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.SecondFactorOnlyAuthenticatorInspector"
-            p:secondFactorOnlyAuthenticators="%{idp.authn.webauthn.registration.authenticator.inspector.secondFactorOnlyAuthenticators:null}"
-            p:activationCondition="%{idp.authn.webauthn.registration.authenticator.inspector.secondFactorOnlyInspector.enabled:true}"/>
-
-    </util:list>
-    
-    
-    <bean id="shibboleth.authn.webauthn.registration.authenticator.policy.AuthenticatorGetInfoUVCapable"
-                    class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.AuthenticatorGetInfoUVCapable" scope="prototype"/>  
+        p:authenticatorInspectorChain="#{getObject('%{idp.authn.webauthn.registration.authenticator.inspector.chainedlist:shibboleth.authn.webauthn.registration.ChainedAuthenticatorInspectorList}')}"/>  
         
     <bean id="ValidateAuthenticatorAttestationResponse" parent="AbstractWebAuthnRegistrationAction"
         class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.ValidateAuthenticatorAttestationResponse" />
@@ -226,7 +212,6 @@
         
    <bean id="InlineEnrolmentRedirectFunction" class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.InlineEnrolmentRedirectFunction"/>    
 
-    
     <!-- Default functions to produce messages for the registration view. -->
     <bean id="DefaultRegistrationInfoMessageFunction" class="net.shibboleth.idp.plugin.authn.webauthn.context.navigate.RegistrationInfoMessageLookupFunction" lazy-init="true"
         p:genericMessageID="%{idp.authn.webauthn.registration.genericMessageID:}" />
@@ -271,5 +256,7 @@
              </map>
         </property>
     </bean>
+    
+    <import resource="conditional:%{idp.home}/conf/authn/webauthn-registration-config.xml" />
 
 </beans>
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-beans.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-beans.xml
index 6538206..f51834f 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-beans.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-beans.xml
@@ -22,6 +22,10 @@
     <bean id="AbstractCredentialPolicyRule" scope="prototype" abstract="true"
         p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.webauthn.DefaultWebAuthnFidoMetadataServiceFactory')}"/>
 
+    <bean id="shibboleth.authn.webauthn.FunctionalCredentialPolicyRule" abstract="true" scope="prototype"
+        class=" net.shibboleth.idp.plugin.authn.webauthn.policy.impl.FunctionalCredentialPolicyRule" 
+        p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.webauthn.DefaultWebAuthnFidoMetadataServiceFactory')}"/>
+        
     <!-- Flow beans -->
     
     <bean id="PopulateWebAuthnAuthenticationContextPasswordless" scope="prototype"
@@ -150,19 +154,10 @@
         class="net.shibboleth.idp.plugin.authn.webauthn.policy.impl.ChainingCredentialPolicyRule"
         p:credentialPolicyChain="#{getObject('%{idp.authn.webauthn.credential.policy.chainedlist:shibboleth.authn.webauthn.ChainedCredentialPolicyList}')}"/>
     
-        
-    <util:list id="shibboleth.authn.webauthn.ChainedCredentialPolicyList">  
-      
-       <bean id="SecondFactorOnlyCredentialPolicyRule" parent="AbstractCredentialPolicyRule" scope="prototype"
-            class="net.shibboleth.idp.plugin.authn.webauthn.policy.impl.SecondFactorOnlyCredentialPolicyRule"
-            p:activationCondition="%{idp.authn.webauthn.registration.credential.policy.secondFactorOnly.enabled:true}"/>
-         
-    </util:list>       
-    
     <bean id="ValidateWebAuthnAssertion" scope="prototype"
         class="net.shibboleth.idp.plugin.authn.webauthn.impl.ValidateWebAuthnAssertion"
         p:webAuthnClient="#{getObject('shibboleth.authn.webauthn.WebAuthnAuthenticationClientFactory') ?: getObject('shibboleth.authn.webauthn.DefaultWebAuthnAuthenticationClientFactory')}" 
-        p:credentialRepository="#{getObject('shibboleth.authn.webauthn.CredentialRepositoryy') ?: getObject('shibboleth.authn.webauthn.DefaultCredentialRepository')}"
+        p:credentialRepository="#{getObject('shibboleth.authn.webauthn.CredentialRepository') ?: getObject('shibboleth.authn.webauthn.DefaultCredentialRepository')}"
         p:updateSignatureCountPredicate="#{getObject('shibboleth.authn.webauthn.UpdateSignatureCountPredicate') ?: %{idp.authn.webauthn.updateSignatureCount:true}}"
         p:populateAuditContextAction="#{%{idp.authn.webauthn.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('shibboleth.authn.webauthn.PopulateAuditContext') : null}"
         p:writeAuditLogAction="#{%{idp.authn.webauthn.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('WriteAuthnAuditLog') : null}"/>
@@ -220,5 +215,7 @@
              </map>
         </property>
     </bean>
+    
+   <import resource="conditional:%{idp.home}/conf/authn/webauthn-config.xml" />
 
 </beans>
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml
index 6608675..868c674 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml
@@ -9,6 +9,15 @@
 
     default-init-method="initialize" default-destroy-method="destroy" default-lazy-init="true">
 
+    <!-- 
+    The default list to add credential policies used during authentication. The property 
+    idp.authn.webauthn.credential.policy.enabled must be set to true for these to take effect.
+    
+    -->
+    <util:list id="shibboleth.authn.webauthn.ChainedCredentialPolicyList">
+        <bean id="SecondFactorOnlyCredentialPolicyRule" parent="AbstractCredentialPolicyRule"
+                class="net.shibboleth.idp.plugin.authn.webauthn.policy.impl.SecondFactorOnlyCredentialPolicyRule"/>
+    </util:list>
 
 
 </beans>
\ No newline at end of file
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-management-config.xml
similarity index 99%
copy from webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml
copy to webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-management-config.xml
index 6608675..92416d5 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-management-config.xml
@@ -10,5 +10,4 @@
     default-init-method="initialize" default-destroy-method="destroy" default-lazy-init="true">
 
 
-
 </beans>
\ No newline at end of file
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration-config.xml b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration-config.xml
new file mode 100644
index 0000000..0dabaeb
--- /dev/null
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration-config.xml
@@ -0,0 +1,32 @@
+<?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" default-lazy-init="true">
+    
+    <!-- 
+    The default list to add authenticator policies used during registration. The property 
+    idp.authn.webauthn.registration.authenticator.policy.enabled must be set to true for these to take effect.
+    -->
+    <util:list id="shibboleth.authn.webauthn.registration.ChainedAuthenticatorPolicyList">  
+        <bean id="AllowlistAuthenticatorPolicy" parent="AbstractAuthenticatorPolicyRule" 
+            class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.AllowlistAuthenticatorPolicy"
+            p:allowedAuthenticators="%{idp.authn.webauthn.registration.authenticator.policy.allowedAuthenticators:null}"/>
+    </util:list>    
+        
+    <!-- 
+    The default list to add authenticator inspectors used during registration. The property 
+    idp.authn.webauthn.registration.authenticator.inspector.enabled must be set to true for these to take effect.
+    -->
+    <util:list id="shibboleth.authn.webauthn.registration.ChainedAuthenticatorInspectorList">  
+        <bean id="shibboleth.authn.webauthn.registration.SecondFactorOnlyInspector"
+            class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.SecondFactorOnlyAuthenticatorInspector"
+            p:secondFactorOnlyAuthenticators="%{idp.authn.webauthn.registration.authenticator.inspector.secondFactorOnlyAuthenticators:null}"/>
+    </util:list>
+
+</beans>
\ No newline at end of file
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration.properties b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration.properties
index fc6dcad..c715af0 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration.properties
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration.properties
@@ -52,22 +52,18 @@
 #idp.authn.webauthn.registration.authenticator.policy.enabled = false
 # Set the authenticator policy to use, defaults to a chained set of policies
 #idp.authn.webauthn.registration.authenticator.policy = shibboleth.authn.webauthn.registration.ChainedAuthenticatorPolicy
-# When using the chained policy, give the name of the list of policies
-#idp.authn.webauthn.registration.authenticator.policy.chainedlist = shibboleth.authn.webauthn.registration.ChainedAuthenticatorPolicies
-# When using the default chained policy, enabled the allowed authenticators policy?
-#idp.authn.webauthn.registration.authenticator.policy.allowedAuthenticators.enabled = true
-# When using the default chained policy, give a comma seperated list of allowed authenticator attestation GUIDs (AAGUID)
+# When using the chained policy; name the policy list bean
+#idp.authn.webauthn.registration.authenticator.policy.chainedlist = shibboleth.authn.webauthn.ChainedCredentialPolicyList
+# When using the default chained policy; list allowed authenticator's by their attestation GUIDs (AAGUID)
 #idp.authn.webauthn.registration.authenticator.policy.allowedAuthenticators = 
 
 # Enable the authenticator inspector engine?
 #idp.authn.webauthn.registration.authenticator.inspector.enabled = false
 # Set the authenticator inspectors to use, defaults to a chained set of inspectors
 #idp.authn.webauthn.registration.authenticator.inspector = shibboleth.authn.webauthn.registration.ChainedAuthenticatorCapabilitiesInspector
-# When using the chained inspectors, give the name of the list of inspectors
+# When using the default chained inspector; name the inspector list bean
 #idp.authn.webauthn.registration.authenticator.inspector.chainedlist = shibboleth.authn.webauthn.registration.ChainedAuthenticatorInspectors
-# When using the default chained set of inspectors, should we enable the 'second factor only' inspector
-#idp.authn.webauthn.registration.authenticator.inspector.secondFactorOnlyInspector.enabled = true
-# When using the default chained set of inspectors, give a comma seperated list of authenticators (by attestation GUIDs (AAGUID)) to tag as only allowed for second factor authentication
+# When using the default chained set of inspectors; list authenticators (by attestation GUIDs (AAGUID)) to tag as only allowed for second factor authentication
 #idp.authn.webauthn.registration.authenticator.inspector.secondFactorOnlyAuthenticators =
 
 # Allow inline self-enrolment 
@@ -91,6 +87,11 @@
 
 #### FIDO Metadata 
 
+# Allow credential registrations to be untrusted, the default. If false, this requires attestation statements from the 
+# authenticator using 'idp.authn.webauthn.registration.attestationConveyancePreference = DIRECT' and FIDO metadata is 
+# loaded and trusted.
+#idp.authn.webauthn.allowUntrustedAttestation = true
+
 # Enable FIDO Alliance metadata? If you want to use the metadata service, you *must* download and set the trust root and the legal blob file headers
 #idp.authn.webauthn.metadata.enabled = false
 #idp.authn.webauthn.metadata.trustRootFile = %{idp.home}/credentials/root-r3.crt
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties
index b9b8c5c..de00cca 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties
@@ -12,11 +12,6 @@ idp.authn.webauthn.relyingPartyName = Shibboleth
 # Allow any subdomain of that origin
 #idp.authn.webauthn.allowOriginSubdomain = false
 
-# Allow credential registrations to be untrusted, the default. If false, this requires attestation statements from the 
-# authenticator using 'idp.authn.webauthn.registration.attestationConveyancePreference = DIRECT' and FIDO metadata is 
-# loaded and trusted.
-#idp.authn.webauthn.allowUntrustedAttestation = true
-
 # Generic flow properties to adjust behavior
 #idp.authn.webauthn.order = 1000
 #idp.authn.webauthn.forcedAuthenticationSupported = true
@@ -69,14 +64,12 @@ idp.authn.webauthn.supportedPrincipals = \
 # The ID of the bean that supplies the c14n flows that are applied to the username entered during the passwordless flow
 #idp.authn.webauthn.passwordless.c14n.postUsernameFlows = shibboleth.PostLoginSubjectCanonicalizationFlows
 
-# Enable the credential/authenticator policy engine
+# Enable the credential policy engine
 #idp.authn.webauthn.credential.policy.enabled = false
 # Set the credential policies to use, defaults to a chained set of policies
 #idp.authn.webauthn.credential.policy = shibboleth.authn.webauthn.ChainedCredentialPolicy
 # When using the default chained policy, which policy list should we use?
-#idp.authn.webauthn.credential.policy.chainedlist = shibboleth.authn.webauthn.ChainedCredentialPoliciesList
-#  When using the default chained policy list, should we enable the 'second-factor only' credential rule
-#idp.authn.webauthn.credential.policy.secondFactorOnly.enabled = true
+#idp.authn.webauthn.credential.policy.chainedlist = shibboleth.authn.webauthn.ChainedCredentialPolicyList
 
 # Audit
 #idp.authn.webauthn.audit.enabled = false
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/module.properties b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/module.properties
index 5cb2bf6..860430e 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/module.properties
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/module.properties
@@ -22,35 +22,43 @@ idp.authn.WebAuthn.3.src = /net/shibboleth/idp/plugin/authn/webauthn/conf/authn/
 idp.authn.WebAuthn.3.dest = conf/authn/webauthn-config.xml
 idp.authn.WebAuthn.3.replace = false
 
-idp.authn.WebAuthn.4.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn.vm
-idp.authn.WebAuthn.4.dest = views/webauthn/webauthn-authn.vm
+idp.authn.WebAuthn.4.src = /net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration-config.xml
+idp.authn.WebAuthn.4.dest = conf/authn/webauthn-registration-config.xml
+idp.authn.WebAuthn.4.replace = false
 
-idp.authn.WebAuthn.5.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
-idp.authn.WebAuthn.5.dest = views/webauthn/webauthn-register.vm
+idp.authn.WebAuthn.5.src = /net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-management-config.xml
+idp.authn.WebAuthn.5.dest = conf/authn/webauthn-management-config.xml
+idp.authn.WebAuthn.5.replace = false
 
-idp.authn.WebAuthn.6.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register-username.vm
-idp.authn.WebAuthn.6.dest = views/webauthn/webauthn-register-username.vm
+idp.authn.WebAuthn.6.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn.vm
+idp.authn.WebAuthn.6.dest = views/webauthn/webauthn-authn.vm
 
-idp.authn.WebAuthn.7.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn-username.vm
-idp.authn.WebAuthn.7.dest = views/webauthn/webauthn-authn-username.vm
+idp.authn.WebAuthn.7.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
+idp.authn.WebAuthn.7.dest = views/webauthn/webauthn-register.vm
 
-idp.authn.WebAuthn.8.src = /net/shibboleth/idp/plugin/authn/webauthn/css/webauthn.css
-idp.authn.WebAuthn.8.dest = edit-webapp/css/webauthn.css
-idp.authn.WebAuthn.8.postenable = Customize edit-webapp/css/webauthn.css and rebuild war to deploy.
+idp.authn.WebAuthn.8.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register-username.vm
+idp.authn.WebAuthn.8.dest = views/webauthn/webauthn-register-username.vm
 
-idp.authn.WebAuthn.9.src = /net/shibboleth/idp/plugin/authn/webauthn/js/webauthn-json.browser-ponyfill.min.js
-idp.authn.WebAuthn.9.dest = edit-webapp/js/webauthn/webauthn-json.browser-ponyfill.min.js
+idp.authn.WebAuthn.9.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn-username.vm
+idp.authn.WebAuthn.9.dest = views/webauthn/webauthn-authn-username.vm
 
-idp.authn.WebAuthn.10.src = /net/shibboleth/idp/plugin/authn/webauthn/js/webauthn-support.js
-idp.authn.WebAuthn.10.dest = edit-webapp/js/webauthn/webauthn-support.js
+idp.authn.WebAuthn.10.src = /net/shibboleth/idp/plugin/authn/webauthn/css/webauthn.css
+idp.authn.WebAuthn.10.dest = edit-webapp/css/webauthn.css
+idp.authn.WebAuthn.10.postenable = Customize edit-webapp/css/webauthn.css and rebuild war to deploy.
 
-idp.authn.WebAuthn.11.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-end.vm
-idp.authn.WebAuthn.11.dest = views/webauthn/webauthn-end.vm
+idp.authn.WebAuthn.11.src = /net/shibboleth/idp/plugin/authn/webauthn/js/webauthn-json.browser-ponyfill.min.js
+idp.authn.WebAuthn.11.dest = edit-webapp/js/webauthn/webauthn-json.browser-ponyfill.min.js
 
-idp.authn.WebAuthn.12.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management.vm
-idp.authn.WebAuthn.12.dest = views/webauthn/webauthn-management.vm
+idp.authn.WebAuthn.12.src = /net/shibboleth/idp/plugin/authn/webauthn/js/webauthn-support.js
+idp.authn.WebAuthn.12.dest = edit-webapp/js/webauthn/webauthn-support.js
 
-idp.authn.WebAuthn.13.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management-search.vm
-idp.authn.WebAuthn.13.dest = views/webauthn/webauthn-management-search.vm
+idp.authn.WebAuthn.13.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-end.vm
+idp.authn.WebAuthn.13.dest = views/webauthn/webauthn-end.vm
+
+idp.authn.WebAuthn.14.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management.vm
+idp.authn.WebAuthn.14.dest = views/webauthn/webauthn-management.vm
+
+idp.authn.WebAuthn.15.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management-search.vm
+idp.authn.WebAuthn.15.dest = views/webauthn/webauthn-management-search.vm
 
 
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn.vm b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn.vm
index 7082884..1f849ec 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn.vm
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn.vm
@@ -118,6 +118,10 @@ $response.addHeader("Content-Security-Policy", "default-src 'none'; style-src 's
             #end
             <div id="supportedDiv">
                 <div class="content">
+                    #set ($errorMessage = $errorMessageFunction.apply($profileRequestContext))
+                    #if ($errorMessage)
+                        <p class="output-message output--error">$encoder.encodeForHTML($errorMessage)</p>        
+                    #end
                     <form id="authenticator_assertion_form" action="$flowExecutionUrl" method="post">
                         #parse("csrf/csrf.vm")
                         <input type="hidden" id="publicKeyCredential" name="publicKeyCredential" />
@@ -156,7 +160,7 @@ $response.addHeader("Content-Security-Policy", "default-src 'none'; style-src 's
                 </ul>
         </section>
         <footer>
-            <div class="container container-footer">
+            <div class="cc">
                 <p class="footer-text">#springMessageText("idp.footer", "Insert your footer text here.")</p>
             </div>
         </footer>
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-end.vm b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-end.vm
index e374d58..b085f7e 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-end.vm
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-end.vm
@@ -63,11 +63,11 @@ $response.addHeader("Content-Security-Policy", "default-src 'none'; style-src 's
                 <p>$encoder.encodeForHTML($desc)</p>
             #end
             <div class="content">
-                    #springMessageText("idp.webauthn.ended", "Your session has ended")                    
+                    #springMessageText("idp.webauthn.ended", "Your session has ended.")                    
             </div>
         </section>
         <footer>
-            <div class="container container-footer">
+            <div class="cc">
                 <p class="footer-text">#springMessageText("idp.footer", "Insert your footer text here.")</p>
             </div>
         </footer>
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestAdminManagementFlow.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestAdminManagementFlow.java
index fd84f63..bca893c 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestAdminManagementFlow.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestAdminManagementFlow.java
@@ -47,7 +47,7 @@ import net.shibboleth.shared.collection.Pair;
 @ContextConfiguration(
         locations = {
                 "classpath*:/META-INF/net.shibboleth.idp/postconfig.xml",
-                "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml", },
+                "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-management.xml", },
         initializers = {
                 TestWebAuthnEnvironmentApplicationContextInitializer.class,
                 TestWebAuthnAdminManagementApplicationContextInitializer.class
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestPasswordlessFlow.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestPasswordlessFlow.java
index aa46e0d..7d05b2d 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestPasswordlessFlow.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestPasswordlessFlow.java
@@ -43,7 +43,7 @@ import net.shibboleth.shared.collection.Pair;
 @ContextConfiguration(
         locations = {
                 "classpath*:/META-INF/net.shibboleth.idp/postconfig.xml",
-                "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml", },
+                "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml", },
         initializers = {
                 TestWebAuthnEnvironmentApplicationContextInitializer.class,
                 TestWebAuthnPasswordlessApplicationContextInitializer.class
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestRegistrationFlow.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestRegistrationFlow.java
index 5ac9de8..82652e2 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestRegistrationFlow.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestRegistrationFlow.java
@@ -52,7 +52,7 @@ import net.shibboleth.shared.collection.Pair;
 @ContextConfiguration(
         locations = {
                 "classpath*:/META-INF/net.shibboleth.idp/postconfig.xml",
-                "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml", },
+                "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-registration.xml", },
         initializers = {
                 TestWebAuthnEnvironmentApplicationContextInitializer.class,
                 TestWebAuthnRegistrationApplicationContextInitializer.class
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestSecondFactorFlow.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestSecondFactorFlow.java
index de94724..c2414f0 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestSecondFactorFlow.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestSecondFactorFlow.java
@@ -47,7 +47,7 @@ import net.shibboleth.shared.collection.Pair;
 @ContextConfiguration(
         locations = {
                 "classpath*:/META-INF/net.shibboleth.idp/postconfig.xml",
-                "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml", },
+                "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml", },
         initializers = {
                 TestWebAuthnEnvironmentApplicationContextInitializer.class,
                 TestWebAuthn2FaApplicationContextInitializer.class
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlow.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlow.java
index 5701886..3405580 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlow.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlow.java
@@ -44,7 +44,7 @@ import net.shibboleth.shared.collection.Pair;
 @ContextConfiguration(
         locations = {
                 "classpath*:/META-INF/net.shibboleth.idp/postconfig.xml",
-                "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml", },
+                "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml", },
         initializers = {
                 TestWebAuthnEnvironmentApplicationContextInitializer.class,
                 TestWebAuthnUsernamelessApplicationContextInitializer.class
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlowWithPolicy.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlowWithPolicy.java
index 30f5da0..c81e3ca 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlowWithPolicy.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlowWithPolicy.java
@@ -42,7 +42,7 @@ import net.shibboleth.shared.collection.Pair;
 @ContextConfiguration(
         locations = {
                 "classpath*:/META-INF/net.shibboleth.idp/postconfig.xml",
-                "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml", },
+                "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml", },
         initializers = {
                 TestWebAuthnEnvironmentApplicationContextInitializer.class,
                 TestWebAuthnUsernamelessWithPolicyApplicationContextInitializer.class
@@ -63,7 +63,6 @@ public class TestUsernameslessFlowWithPolicy extends AbstractWebAuthnFlowTest{
     @SuppressWarnings("null")
     @Test
     public void testUsernamelessFlow_CredentialRejected_2FAOnly() throws Exception {
-        
         //Register a credential for use that is only suitable for 2FA
         final CredentialRegistration registration = 
                 createCredentialRegistration(USERNAME, DISPLAY_NAME, USER_HANDLE_B64, 
diff --git a/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml b/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml
similarity index 58%
copy from webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml
copy to webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml
index ca9b264..419ca14 100644
--- a/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml
+++ b/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml
@@ -13,8 +13,20 @@
                            
        default-init-method="initialize"
        default-destroy-method="destroy">
+       
+    <bean id="AbstractCredentialPolicyRule" scope="prototype" abstract="true"
+        p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.webauthn.DefaultWebAuthnFidoMetadataServiceFactory')}"/>
+    
 
-      
-   
+    <!-- 
+    The default list to add credential policies used during authentication. The property 
+    idp.authn.webauthn.credential.policy.enabled must be set to true for these to take effect.
+    
+    -->
+    <util:list id="shibboleth.authn.webauthn.ChainedCredentialPolicyList">
+        <bean id="SecondFactorOnlyCredentialPolicyRule" parent="AbstractCredentialPolicyRule"
+                class="net.shibboleth.idp.plugin.authn.webauthn.policy.impl.SecondFactorOnlyCredentialPolicyRule"/>
+    </util:list>
+       
 
 </beans>
diff --git a/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml b/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-management.xml
similarity index 99%
rename from webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml
rename to webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-management.xml
index ca9b264..8065e84 100644
--- a/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml
+++ b/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-management.xml
@@ -13,8 +13,6 @@
                            
        default-init-method="initialize"
        default-destroy-method="destroy">
-
-      
    
 
 </beans>
diff --git a/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-registration.xml b/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-registration.xml
new file mode 100644
index 0000000..0b472bd
--- /dev/null
+++ b/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-registration.xml
@@ -0,0 +1,47 @@
+<?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:jdbc="http://www.springframework.org/schema/jdbc"
+       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/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.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="AbstractWebAuthnRegistrationAction" scope="prototype" abstract="true"
+        p:webAuthnClient="#{getObject('shibboleth.authn.webauthn.WebAuthnAuthenticationClientFactory') ?: getObject('shibboleth.authn.webauthn.DefaultWebAuthnAuthenticationClientFactory')}"
+        p:credentialRepository="#{getObject('shibboleth.authn.webauthn.CredentialRepositoryy') ?: getObject('shibboleth.authn.webauthn.DefaultCredentialRepository')}"
+        p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.webauthn.DefaultWebAuthnFidoMetadataServiceFactory')}"/>
+  
+    <bean id="AbstractAuthenticatorPolicyRule" scope="prototype" abstract="true"
+        p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.webauthn.DefaultWebAuthnFidoMetadataServiceFactory')}"/>
+    
+
+    <!-- 
+    The default list to add authenticator policies used during registration. The property 
+    idp.authn.webauthn.registration.authenticator.policy.enabled must be set to true for these to take effect.
+    -->
+    <util:list id="shibboleth.authn.webauthn.registration.ChainedAuthenticatorPolicyList">  
+        <bean id="AllowlistAuthenticatorPolicy" parent="AbstractAuthenticatorPolicyRule" 
+            class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.AllowlistAuthenticatorPolicy"
+            p:allowedAuthenticators="%{idp.authn.webauthn.registration.authenticator.policy.allowedAuthenticators:null}"/>
+    </util:list>    
+        
+    <!-- 
+    The default list to add authenticator inspectors used during registration. The property 
+    idp.authn.webauthn.registration.authenticator.inspector.enabled must be set to true for these to take effect.
+    -->
+    <util:list id="shibboleth.authn.webauthn.registration.ChainedAuthenticatorInspectorList">  
+        <bean id="shibboleth.authn.webauthn.registration.SecondFactorOnlyInspector" scope="prototype"
+            class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.SecondFactorOnlyAuthenticatorInspector"
+            p:secondFactorOnlyAuthenticators="%{idp.authn.webauthn.registration.authenticator.inspector.secondFactorOnlyAuthenticators:null}"/>
+    </util:list>
+   
+
+</beans>

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


More information about the commits mailing list