[java-idp-plugin-webauthn] branch main updated: Allow username to passthrough to WebAuthn flow from registration flow

Phil Smart philip.smart at jisc.ac.uk
Fri Feb 23 17:27:06 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=b1c6c72b1a078b69fd8b89e6cb0f3661551e69a4

The following commit(s) were added to refs/heads/main by this push:
     new b1c6c72  Allow username to passthrough to WebAuthn flow from registration flow
b1c6c72 is described below

commit b1c6c72b1a078b69fd8b89e6cb0f3661551e69a4
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Feb 23 17:27:03 2024 +0000

    Allow username to passthrough to WebAuthn flow from registration flow
    
     - If exists, to avoid entering the username twice
---
 ... => UsernameLookupFromRegistrationContext.java} | 24 +++++++-------
 .../navigate/UsernameLookupFromSubjectContext.java |  3 +-
 ...ava => YubicoWebAuthnAuthenticationClient.java} |  9 ++---
 .../client/impl/YubicoWebauthnClientFactory.java   | 16 ++++-----
 .../storage/impl/InMemoryRegistrationStorage.java  |  1 +
 .../idp/flows/authn/WebAuthn/webauthn-beans.xml    |  7 +++-
 .../idp/flows/authn/WebAuthn/webauthn-flow.xml     |  2 +-
 .../webauthn/views/webauthn-authn-username.vm      | 16 ++-------
 .../plugin/authn/webauthn/views/webauthn-authn.vm  | 38 +++++++++++-----------
 .../webauthn/views/webauthn-register-username.vm   | 38 +++++++---------------
 .../authn/webauthn/views/webauthn-register.vm      | 38 +++++++++++-----------
 .../YubicoWebauthnAuthenticationClientTest.java    |  6 ++--
 .../impl/ValidatePublicKeyCredentialTest.java      | 11 +++++--
 13 files changed, 98 insertions(+), 111 deletions(-)

diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/UsernameLookupFromSubjectContext.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/UsernameLookupFromRegistrationContext.java
similarity index 59%
copy from webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/UsernameLookupFromSubjectContext.java
copy to webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/UsernameLookupFromRegistrationContext.java
index effb05b..d390b93 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/UsernameLookupFromSubjectContext.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/UsernameLookupFromRegistrationContext.java
@@ -22,17 +22,17 @@ import javax.annotation.Nullable;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 
-import net.shibboleth.idp.authn.context.SubjectContext;
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
 import net.shibboleth.shared.primitive.LoggerFactory;
 
 /**
- * Pull out a username from a the {@link SubjectContext#getPrincipalName()} if it exists. 
- * Useful when operating inside a WebAuthn registration flow.
+ * Pull out a username from a WebAuthn Registration Context if it exists. Useful when operating inside a WebAuthn 
+ * registration flow.
  */
-public class UsernameLookupFromSubjectContext implements Function<ProfileRequestContext, String> {
+public class UsernameLookupFromRegistrationContext implements Function<ProfileRequestContext, String> {
     
     /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(UsernameLookupFromSubjectContext.class);
+    @Nonnull private final Logger log = LoggerFactory.getLogger(UsernameLookupFromRegistrationContext.class);
 
     /** {@inheritDoc} */
     @Override
@@ -41,13 +41,15 @@ public class UsernameLookupFromSubjectContext implements Function<ProfileRequest
             log.trace("Profile context was null, can not find existing username");
             return null;
         }
-        final SubjectContext subjectContext = input.getSubcontext(SubjectContext.class);
-        if (subjectContext == null) {
-            log.debug("Subject context was null, can not find existing username");
+        final WebAuthnRegistrationContext registrationContext = 
+                input.getSubcontext(WebAuthnRegistrationContext.class);
+        if (registrationContext == null) {
+            log.trace("WebAuthn registration context was null, can not find existing username");
             return null;
-        }    
-        final String username = subjectContext.getPrincipalName();
-        log.debug("Found existing username '{}' from subject", username);
+        }
+        final String username = registrationContext.getUsername();
+        log.debug("{}", username != null ? "Found existing username from registration context" : 
+            "Did not find existing username from registration context");
         return username;
     }
 
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/UsernameLookupFromSubjectContext.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/UsernameLookupFromSubjectContext.java
index effb05b..18393ab 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/UsernameLookupFromSubjectContext.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/navigate/UsernameLookupFromSubjectContext.java
@@ -26,8 +26,7 @@ import net.shibboleth.idp.authn.context.SubjectContext;
 import net.shibboleth.shared.primitive.LoggerFactory;
 
 /**
- * Pull out a username from a the {@link SubjectContext#getPrincipalName()} if it exists. 
- * Useful when operating inside a WebAuthn registration flow.
+ * Pull out a username from a the {@link SubjectContext#getPrincipalName()} if it exists.
  */
 public class UsernameLookupFromSubjectContext implements Function<ProfileRequestContext, String> {
     
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnAuthenticationClient.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebAuthnAuthenticationClient.java
similarity index 97%
rename from webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnAuthenticationClient.java
rename to webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebAuthnAuthenticationClient.java
index d0ef7b0..ea934ed 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnAuthenticationClient.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebAuthnAuthenticationClient.java
@@ -59,10 +59,10 @@ import net.shibboleth.shared.primitive.LoggerFactory;
  * <p>Thread-safe, only a single instance is required.</p>
  */
 @ThreadSafe
-public class YubicoWebauthnAuthenticationClient implements WebAuthnAuthenticationClient {
+public class YubicoWebAuthnAuthenticationClient implements WebAuthnAuthenticationClient {
     
     /** Class logger.*/
-    @Nonnull private final Logger log = LoggerFactory.getLogger(YubicoWebauthnAuthenticationClient.class);
+    @Nonnull private final Logger log = LoggerFactory.getLogger(YubicoWebAuthnAuthenticationClient.class);
     
     /** Information pertaining to the relying party.*/
     @Nonnull private final RelyingParty rp;
@@ -71,14 +71,15 @@ public class YubicoWebauthnAuthenticationClient implements WebAuthnAuthenticatio
     @Nonnull @NonnullElements @NotLive private final List<PublicKeyCredentialParameters> preferredPublickeyParams;
     
     /**
+     * Package-private Constructor.
      * 
-     * Constructor.
+     * <p>Should only be instantiated by the {@link YubicoWebauthnClientFactory}.</p>
      *
      * @param relyingParty information pertaining to the relying party.
      * @param publickeyParams list of preferred public key credential parameters to send to the authenticator 
      *                                  during registration.
      */
-    public YubicoWebauthnAuthenticationClient(@Nonnull final RelyingParty relyingParty,
+    YubicoWebAuthnAuthenticationClient(@Nonnull final RelyingParty relyingParty,
             @Nonnull @NonnullElements @NotLive final List<PublicKeyCredentialParameters> publickeyParams) {
         rp = Constraint.isNotNull(relyingParty, "The reyling party configuration can not be null");
         preferredPublickeyParams = Constraint.isNotNull(publickeyParams, "PreferredPublickeyParams can not be null");
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnClientFactory.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnClientFactory.java
index fe5065a..5fa6d0c 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnClientFactory.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnClientFactory.java
@@ -45,7 +45,7 @@ import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.StringSupport;
 
 /**
- * Spring factory beans for creating a {@link YubicoWebauthnAuthenticationClient}.
+ * Spring factory beans for creating a {@link YubicoWebAuthnAuthenticationClient}.
  */
 @ThreadSafe
 public class YubicoWebauthnClientFactory extends AbstractInitializableComponent 
@@ -106,8 +106,8 @@ public class YubicoWebauthnClientFactory extends AbstractInitializableComponent
     }
     
     @Override
-    public WebAuthnAuthenticationClient getObject() throws Exception {
-        
+    public WebAuthnAuthenticationClient getObject() throws Exception {        
+        checkComponentActive();
         // FIXME: There is an issue in the builder here than prevents origins from being set as null
         // once that is fixed, we only need one builder statement here.
         final var builder = RelyingParty.builder().identity(
@@ -122,11 +122,11 @@ public class YubicoWebauthnClientFactory extends AbstractInitializableComponent
         if (!getOrigins().isEmpty()) {            
             final RelyingParty rp = builder.origins(getOrigins()).build();
             assert rp != null;
-            return new YubicoWebauthnAuthenticationClient(rp, getPreferredPublickeyParams());
+            return new YubicoWebAuthnAuthenticationClient(rp, getPreferredPublickeyParams());
         } else {
             final RelyingParty rp = builder.build();
             assert rp != null;
-            return new YubicoWebauthnAuthenticationClient(rp, getPreferredPublickeyParams());
+            return new YubicoWebAuthnAuthenticationClient(rp, getPreferredPublickeyParams());
         }        
         
     }
@@ -293,7 +293,7 @@ public class YubicoWebauthnClientFactory extends AbstractInitializableComponent
     }
 
     /**
-     * Is origin port allowed?
+     * Is origin matching relaxed to allow any port number?
      * 
      * @return allow origin port?
      */
@@ -313,7 +313,7 @@ public class YubicoWebauthnClientFactory extends AbstractInitializableComponent
     }
 
     /**
-     * Are subdomains allowed?
+     * Is origin matching relaxed to allow any subdomain?
      * 
      * @return allow subdomains?
      */
@@ -323,7 +323,7 @@ public class YubicoWebauthnClientFactory extends AbstractInitializableComponent
     }
 
     /**
-     * Set if subdomains are allowed.
+     * Set if any subdomain is allowed.
      * 
      * @param allow are subdomains allowed?
      */
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/impl/InMemoryRegistrationStorage.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/impl/InMemoryRegistrationStorage.java
index 4f44e5e..5a82ec2 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/impl/InMemoryRegistrationStorage.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/impl/InMemoryRegistrationStorage.java
@@ -50,6 +50,7 @@ import net.shibboleth.idp.plugin.authn.webauthn.storage.StorageServiceCredential
 /**
  * Use {@link IdPStorageServiceCredentialRespository}
  */
+// move to test package
 @Deprecated
 public class InMemoryRegistrationStorage implements StorageServiceCredentialRepository {
 
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 5a4e552..77c68b6 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
@@ -16,7 +16,12 @@
 
 
     <bean id="PopulateWebAuthnAuthenticationContextPasswordless" scope="prototype"
-        class="net.shibboleth.idp.plugin.authn.webauthn.impl.PopulateWebAuthnAuthenticationContext"/>
+        class="net.shibboleth.idp.plugin.authn.webauthn.impl.PopulateWebAuthnAuthenticationContext">
+        <property name="usernameLookupStrategy">
+            <bean id="usernameFromAuthnResult" scope="prototype"
+                class="net.shibboleth.idp.plugin.authn.webauthn.context.navigate.UsernameLookupFromRegistrationContext"/>
+        </property>
+    </bean>
     
     <bean id="PopulateWebAuthnAuthenticationContextUsernameless" scope="prototype"
         class="net.shibboleth.idp.plugin.authn.webauthn.impl.PopulateWebAuthnAuthenticationContext"/>   
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-flow.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-flow.xml
index add3992..0fe80d0 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-flow.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-flow.xml
@@ -2,7 +2,7 @@
 	xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
     parent="authn.abstract, authn/conditions">
     
-     <!-- Test if we are operating as a 2FA -->
+     <!-- Test if we are operating as 2FA -->
      <decision-state id="DetermineSecondFactorLogin">
         <if test="IsSecondFactor.test(opensamlProfileRequestContext)"
             then="SecondFactorLogin" 
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn-username.vm b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn-username.vm
index a8f647b..47bd8d6 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn-username.vm
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn-username.vm
@@ -7,7 +7,7 @@
 ## flowExecutionKey - the SWF execution key (this is built into the flowExecutionUrl)
 ## profileRequestContext - root of context tree
 ## authenticationContext - context with authentication request information
-## passwordlessContext - context with Duo username and enrollment status
+## webauthnContext - context for WebAuthn authentication
 ## rpUIContext - the context with SP UI information from the metadata
 ## encoder - HTMLEncoder class
 ## cspDigester - Calculates base64-encoded SHA-2 hashes (call apply)
@@ -76,7 +76,7 @@ $response.addHeader("Content-Security-Policy", "script-src-elem 'nonce-$nonce'")
                     #parse("csrf/csrf.vm")
                     
                     <label for="username">#springMessageText("idp.login.username", "Username")</label>
-                    <input id="j_username" name="j_username" type="text" autoComplete="username webauthn"
+                    <input id="j_username" name="j_username" type="text" required
                         value="#if($username)$encoder.encodeForHTML($username)#end" />
                         
                     <input type="checkbox" name="donotcache" value="1" id="donotcache" />
@@ -104,16 +104,6 @@ $response.addHeader("Content-Security-Policy", "script-src-elem 'nonce-$nonce'")
             <div class="cc">
                 <p>#springMessageText("idp.footer", "Insert your footer text here.")</p>
             </div>
-        </footer>
-        
-        <script #if ($nonce)nonce="$nonce"#end>
-        <!--
-        const input = document.getElementById('j_username');
-        const end = input.value.length;
-        input.setSelectionRange(end, end);
-        input.focus();
-        // -->
-        </script>
-        
+        </footer>        
      </body>
 </html>
\ No newline at end of file
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 5fef529..db58167 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
@@ -28,9 +28,9 @@
         <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
         <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0">
         <script type="text/javascript" src="$request.getContextPath()/js/webauthn/webauthn-support.js"></script>
-        <link rel="stylesheet" type="text/css" href="$request.getContextPath()#springMessageText("
-        idp.css", "/css/placeholder.css" )">
+        <link rel="stylesheet" type="text/css" href="$request.getContextPath()#springMessageText("idp.css", "/css/placeholder.css" )">
         <link rel="stylesheet" type="text/css" href="$request.getContextPath()/css/webauthn.css">
+        
         <script type="module">
             import {
                 get,
@@ -69,8 +69,7 @@
                         document.getElementById('supportedDiv').classList.add('hidden')
                         document.getElementById("supportedDiv").disabled = true;
                         document.getElementById("authenticate").disabled = true;
-                        document.getElementById("authenticatorAssertionForm").disabled = true;
-                        document.getElementById("authenticationSubmit").disabled = true;
+                        document.getElementById("authenticator_assertion_form").disabled = true;
                         document.getElementById("unsupportedDiv").disabled = false;
                         document.getElementById('unsupportedDiv').classList.remove('hidden')
                     } else {
@@ -90,7 +89,7 @@
             idp.logo", "/images/placeholder-logo.png" )" alt="#springMessageText(" idp.logo.alt-text", "logo" )" />
             #set ($serviceName = $rpUIContext.serviceName)
             #if ($serviceName && !$rpContext.getRelyingPartyId().contains($serviceName))
-            <h1>#springMessageText("idp.login.loginTo", "Login to") $encoder.encodeForHTML($serviceName)</h1>
+                <h1>#springMessageText("idp.login.loginTo", "Login to") $encoder.encodeForHTML($serviceName)</h1>
             #end
         </header>
         <section>
@@ -107,16 +106,16 @@
             *#
             #set ($logo = $rpUIContext.getLogo())
             #if ($logo)
-            <img class="service-logo" src="$encoder.encodeForHTMLAttribute($logo)"
-                alt="$encoder.encodeForHTMLAttribute($serviceName)">
+                <img class="service-logo" src="$encoder.encodeForHTMLAttribute($logo)"
+                    alt="$encoder.encodeForHTMLAttribute($serviceName)">
             #end
             #set ($desc = $rpUIContext.getServiceDescription())
             #if ($desc)
-            <p>$encoder.encodeForHTML($desc)</p>
+                <p>$encoder.encodeForHTML($desc)</p>
             #end
             <div id="supportedDiv">
                 <div class="content">
-                    <form id="authenticatorAssertionForm" action="$flowExecutionUrl" method="post">
+                    <form id="authenticator_assertion_form" action="$flowExecutionUrl" method="post">
                         #parse("csrf/csrf.vm")
                         <input type="hidden" id="publicKeyAssertion" name="publicKeyAssertion" />
                         <button class="hidden" id="authenticationSubmit" type="submit"
@@ -128,18 +127,19 @@
                             class="form-element form-button">#springMessageText("idp.webauthn.authn.authenticate",
                         "Login with passkey or security key")</button>
                     </div>
+                    
                     #if($debug == "true")
-                    <hr />
-                    <button type="button" class="collapsible">#springMessageText("idp.webauthn.debug.title",
-                    "Debugging")</button>    
-                    <div class="debug" id="debug-div">
-                        <label for="publicKeyCredentialCreation">#springMessageText("idp.webauthn.debug.request",
-                        "Request Options")</label>
-                        <textarea id="publicKeyCredentialRequestOptions" name="publicKeyCredentialRequestOptions"
-                            rows="20" cols="50">
-                        $webauthnContext.publicKeyCredentialRequestOptionsJSON</textarea>
-                    </div>
+                        <hr />
+                        <button type="button" class="collapsible">#springMessageText("idp.webauthn.debug.title","Debugging")</button>    
+                        <div class="debug" id="debug-div">
+                            <label for="publicKeyCredentialCreation">#springMessageText("idp.webauthn.debug.request",
+                            "Request Options")</label>
+                            <textarea id="publicKeyCredentialRequestOptions" name="publicKeyCredentialRequestOptions"
+                                rows="20" cols="50">
+                            $webauthnContext.publicKeyCredentialRequestOptionsJSON</textarea>
+                        </div>
                     #end
+                    
                 </div>
             </div>
             <div id="unsupportedDiv" class="hidden">
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register-username.vm b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register-username.vm
index 089db99..1c3519a 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register-username.vm
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register-username.vm
@@ -1,12 +1,12 @@
 ##
-## Velocity Template for collection of username for Duo Passwordless use
+## Velocity Template for collection of username for WebAuthn registration use
 ##
 ## Velocity context will contain the following properties
 ## flowExecutionUrl - the form action location
 ## flowRequestContext - the Spring Web Flow RequestContext
 ## flowExecutionKey - the SWF execution key (this is built into the flowExecutionUrl)
 ## profileRequestContext - root of context tree
-## rpUIContext - the context with SP UI information from the metadata
+## webauthnRegContext - the WebAuthn registration context
 ## encoder - HTMLEncoder class
 ## cspDigester - Calculates base64-encoded SHA-2 hashes (call apply)
 ## cspNonce - Calculates secure nonces (call generateIdentifier)
@@ -15,17 +15,18 @@
 ## environment - Spring Environment object for property resolution
 ## custom - arbitrary object injected by deployer
 ##
-#set ($rpContext = $profileRequestContext.getSubcontext('net.shibboleth.profile.context.RelyingPartyContext'))
 #set ($eventCtx = $profileRequestContext.getSubcontext('org.opensaml.profile.context.EventContext'))
 #if ($eventCtx)
 #set ($eventId = $eventCtx.getEvent())
 #end
 #set ($nonce = $cspNonce.generateIdentifier())
+
+### TODO ADD THIS BACK
 $response.addHeader("Content-Security-Policy", "script-src-elem 'nonce-$nonce'")
 #set ($onClick = "document.forms.password.j_username.value = document.forms.passwordless.j_username.value")
-### TODO ADD THIS BACK
 ###$response.addHeader("Content-Security-Policy", "script-src-attr 'unsafe-hashes' 'sha256-$cspDigester.apply($onClick)'")
-##
+
+
 <!DOCTYPE html>
 <html>
     <head>
@@ -38,17 +39,11 @@ $response.addHeader("Content-Security-Policy", "script-src-elem 'nonce-$nonce'")
     <body onLoad="$onLoad">
         <main class="main">
             <header>
-                <img class="main-logo" src="$request.getContextPath()#springMessageText("idp.logo", "/images/placeholder-logo.png")" alt="#springMessageText("idp.logo.alt-text", "logo")" />
-                
-                
-            </header>
-            
-            <section>               
-
+                <img class="main-logo" src="$request.getContextPath()#springMessageText("idp.logo", "/images/placeholder-logo.png")" alt="#springMessageText("idp.logo.alt-text", "logo")" />                
+            </header>            
+            <section>  
                 <blockquote>#springMessageText("idp.webauthn.register.username.explain", "Please enter your username below and press the corresponding button.")</blockquote>
 
-
-                
                 #if ($eventId == "RequestUnsupported")
                     <p class="output-message output--error">$encoder.encodeForHTML("#springMessageText('idp.duo.passwordless.unsupported', 'You have not enrolled a qualifying device for Passwordless use.')")</p>
                 #end
@@ -57,13 +52,12 @@ $response.addHeader("Content-Security-Policy", "script-src-elem 'nonce-$nonce'")
                     #parse("csrf/csrf.vm")
                     
                     <label for="username">#springMessageText("idp.login.username", "Username")</label>
-                    <input id="j_username" name="j_username" type="text" autoComplete="username webauthn"
+                    <input id="j_username" name="j_username" type="text" required
                         value="#if($username)$encoder.encodeForHTML($username)#end" />
                         
                     <input type="checkbox" name="donotcache" value="1" id="donotcache" />
                     <label for="donotcache">#springMessageText("idp.login.donotcache", "Don't Remember Login")</label>
 
-                    
                     <div class="grid">
                         <div class="grid-item">
                             <button type="submit" name="_eventId_proceed"
@@ -81,16 +75,6 @@ $response.addHeader("Content-Security-Policy", "script-src-elem 'nonce-$nonce'")
             <div class="cc">
                 <p>#springMessageText("idp.footer", "Insert your footer text here.")</p>
             </div>
-        </footer>
-        
-        <script #if ($nonce)nonce="$nonce"#end>
-        <!--
-        const input = document.getElementById('j_username');
-        const end = input.value.length;
-        input.setSelectionRange(end, end);
-        input.focus();
-        // -->
-        </script>
-        
+        </footer>        
      </body>
 </html>
\ No newline at end of file
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
index 06b96e0..e4f9693 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
@@ -60,24 +60,24 @@
           }
           
           window.addEventListener("load", () => {
-         try {
-           var isSupported = supported();
-           if (!isSupported){
-             document.getElementById('supportedDiv').classList.add('hidden')
-             document.getElementById("supportedDiv").disabled = true;
-             document.getElementById("authenticatorAttestationForm").disabled = true;        
-             document.getElementById("registrationSubmit").disabled = true;
-             
-             document.getElementById("unsupportedDiv").disabled = false;
-             document.getElementById('unsupportedDiv').classList.remove('hidden')
-           } else{
-               initButton();
-           }     
-         
-         } catch (e) {
-         console.error(e);
-         }
-         });    
+                 try {
+                   var isSupported = supported();
+                   if (!isSupported){
+                     document.getElementById('supportedDiv').classList.add('hidden')
+                     document.getElementById("supportedDiv").disabled = true;
+                     document.getElementById("authenticatorAttestationForm").disabled = true;        
+                     document.getElementById("registrationSubmit").disabled = true;
+                     
+                     document.getElementById("unsupportedDiv").disabled = false;
+                     document.getElementById('unsupportedDiv').classList.remove('hidden')
+                   } else{
+                       initButton();
+                   }     
+                 
+                 } catch (e) {
+                 console.error(e);
+                 }
+             });    
       </script>      
    </head>
    <body>
@@ -110,7 +110,7 @@
                                <td>$encoder.encodeForHTML($webAuthnEncoder.formatDiscoverable($cred.isDiscoverable()))</td>
                                <td>$encoder.encodeForHTML($webAuthnEncoder.formatInstant($cred.registrationTime))</td>
                                <td>
-                                  <form id="deleteKeyForm" action="$flowExecutionUrl" method="post">
+                                  <form id="delete_key_form" action="$flowExecutionUrl" method="post">
                                      #parse("csrf/csrf.vm")
                                      <input type="hidden" name="credentialId" value="$cred.credentialIdBase64Url"/>
                                      <button class="webauthn-table-button" onclick="return confirm('#springMessageText("idp.webauthn.register.credential.remove.confirm", "Are you sure")');" id="removeButton" type="submit" name="_eventId_deleteKey">#springMessageText("idp.webauthn.register.credential.remove", "Remove")</button>
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnAuthenticationClientTest.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnAuthenticationClientTest.java
index 90ecdd2..e86adf0 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnAuthenticationClientTest.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnAuthenticationClientTest.java
@@ -51,14 +51,14 @@ import net.shibboleth.idp.plugin.authn.webauthn.storage.impl.InMemoryRegistratio
 import net.shibboleth.shared.codec.Base64Support;
 
 /**
- * Tests for {@link YubicoWebauthnAuthenticationClient}. To some extend this is testing the Yubico libraries work
+ * Tests for {@link YubicoWebAuthnAuthenticationClient}. To some extend this is testing the Yubico libraries work
  * correctly. But it does ensure the client has been constructed to use those libraries correctly.
  */
 public class YubicoWebauthnAuthenticationClientTest extends AbstractWebAuthnTest {    
     
     private final static String CHALLENGE_2_B64 = "8gneM8yvE20CqnSCUkyD";
     
-    private YubicoWebauthnAuthenticationClient client;   
+    private YubicoWebAuthnAuthenticationClient client;   
     
     private PublicKeyCredentialCreationOptions credentialCreationOptions;
     
@@ -85,7 +85,7 @@ public class YubicoWebauthnAuthenticationClientTest extends AbstractWebAuthnTest
             .allowOriginPort(true)
             .allowOriginSubdomain(true)
             .build();
-        client = new YubicoWebauthnAuthenticationClient(rp, preferredPublickeyParams);
+        client = new YubicoWebAuthnAuthenticationClient(rp, preferredPublickeyParams);
         
         userIdentity = 
                 UserIdentity.builder().name(USERNAME).displayName("test user")
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ValidatePublicKeyCredentialTest.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ValidatePublicKeyCredentialTest.java
index 8561019..3770d93 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ValidatePublicKeyCredentialTest.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ValidatePublicKeyCredentialTest.java
@@ -27,7 +27,7 @@ import com.yubico.webauthn.data.UserIdentity;
 
 import net.shibboleth.idp.plugin.authn.webauthn.admin.impl.ValidateAuthenticatorAttestationResponse;
 import net.shibboleth.idp.plugin.authn.webauthn.client.WebAuthnAuthenticationClient;
-import net.shibboleth.idp.plugin.authn.webauthn.client.impl.YubicoWebauthnAuthenticationClient;
+import net.shibboleth.idp.plugin.authn.webauthn.client.impl.YubicoWebauthnClientFactory;
 import net.shibboleth.idp.plugin.authn.webauthn.storage.impl.InMemoryRegistrationStorage;
 import net.shibboleth.shared.codec.Base64Support;
 
@@ -73,8 +73,13 @@ public class ValidatePublicKeyCredentialTest extends AbstractWebAuthnTest{
                     .timeout(Optional.empty()).build();
 
         webAuthnRegContext.setPublicKeyCredentialCreationOptions(credentialCreationOptions);
-                
-        final WebAuthnAuthenticationClient client = new YubicoWebauthnAuthenticationClient(rp, preferredPublickeyParams);
+        final YubicoWebauthnClientFactory factory = new YubicoWebauthnClientFactory();
+        factory.setPreferredPublickeyParams(preferredPublickeyParams.stream().map(alg -> alg.getAlg().name()).toList());
+        factory.setCredentialRepository(new InMemoryRegistrationStorage());
+        factory.setRelyingPartyId("idp.example.com");
+        factory.setRelyingPartyName("Demo IdP as a WebAuthn RP");
+        factory.initialize();     
+        final WebAuthnAuthenticationClient client = factory.getObject();
         validator.setWebAuthnClient(client);
         validator.setCredentialRepository(new InMemoryRegistrationStorage());
         validator.initialize();

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


More information about the commits mailing list