[java-idp-plugin-webauthn] branch main updated: Fix user handle creation so it honours any existing userhandle stored

Phil Smart philip.smart at jisc.ac.uk
Fri Feb 16 14:21:51 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=0bde56b7e27bbc527a644c663621bfe2a77fcafd

The following commit(s) were added to refs/heads/main by this push:
     new 0bde56b  Fix user handle creation so it honours any existing userhandle stored
0bde56b is described below

commit 0bde56b7e27bbc527a644c663621bfe2a77fcafd
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Feb 16 14:21:49 2024 +0000

    Fix user handle creation so it honours any existing userhandle stored
---
 .../impl/AbstractWebAuthnRegistrationAction.java   |  4 +-
 .../webauthn/admin/impl/GenerateUserHandle.java    | 55 ++++++++++++++++++----
 2 files changed, 47 insertions(+), 12 deletions(-)

diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AbstractWebAuthnRegistrationAction.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AbstractWebAuthnRegistrationAction.java
index f24441c..946b65e 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AbstractWebAuthnRegistrationAction.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AbstractWebAuthnRegistrationAction.java
@@ -67,7 +67,6 @@ public abstract class AbstractWebAuthnRegistrationAction extends AbstractProfile
     @NonnullBeforeExec private WebAuthnAuthenticationClient webAuthnClient;
     
     /** The credential respository to store valid credentials in.*/
-    // TODO replace with an adaptor to the storage service?
     @NonnullAfterInit private StorageServiceCredentialRepository credentialRepository;
     
     
@@ -128,8 +127,7 @@ public abstract class AbstractWebAuthnRegistrationAction extends AbstractProfile
      * 
      * @return the credential repository.
      */
-    //TODO should even storage operations go through the client. 
-    public StorageServiceCredentialRepository getCredentialRepository() {
+    @NonnullAfterInit public StorageServiceCredentialRepository getCredentialRepository() {
         return credentialRepository;
     }
     
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/GenerateUserHandle.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/GenerateUserHandle.java
index 8df48a5..4c3bb4e 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/GenerateUserHandle.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/GenerateUserHandle.java
@@ -19,6 +19,7 @@ package net.shibboleth.idp.plugin.authn.webauthn.admin.impl;
 
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
+import java.util.Optional;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
@@ -29,15 +30,21 @@ import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 
+import com.yubico.webauthn.data.ByteArray;
+
+import net.shibboleth.idp.authn.AuthnEventIds;
 import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
 
 /**
- * An action to generate a UserHandle as the user.id. This is used by the IdP to map a public key credential to a 
- * users session map of public keys, and by the Authenticator to map the IdP's ID (RelyingParty ID) and the 
+ * An action to generate or lookup a UserHandle as the user.id. This is used by the IdP to map a public key credential 
+ * to a users session map of public keys, and by the Authenticator to map the IdP's ID (RelyingParty ID) and the 
  * User Handle to a public key credential source (which contains the private key).
  * 
+ * <p>The same user should use the same UserHandle.</p>
+ * 
  * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
  * @event {@link org.opensaml.profile.action.EventIds#INVALID_PROFILE_CTX}
  * @post a UserHandle is added to the registration context
@@ -50,6 +57,9 @@ public class GenerateUserHandle extends AbstractWebAuthnRegistrationAction {
     /** Strategy used to generate the UserHandle. */
     @Nonnull private Function<ProfileRequestContext,byte[]> userHandleGeneratorStrategy;
     
+    /** The stashed username.*/
+    @NonnullBeforeExec private String username;
+    
     /** Constructor. */
     public GenerateUserHandle() {
         userHandleGeneratorStrategy = new DefaultUserHandleGenerator();
@@ -66,19 +76,46 @@ public class GenerateUserHandle extends AbstractWebAuthnRegistrationAction {
         userHandleGeneratorStrategy =
                 Constraint.isNotNull(strategy, "Challenge Generator cannot be null");
     }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final WebAuthnRegistrationContext context) {
+        
+        if (!super.doPreExecute(profileRequestContext, context)) {
+            return false;
+        }
+       
+        username = context.getUsername();
+        if (username == null) {
+            log.error("{} Username not available in registration context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
+            return false;
+        }
+        
+        return true;
+    }
 
     /** {@inheritDoc} */
     @Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
             @Nonnull final WebAuthnRegistrationContext context) {
         
-        final byte[] userHandle = userHandleGeneratorStrategy.apply(profileRequestContext);
-        if (userHandle == null) {
-            log.trace("{} Generated UserHandle was null", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
-            return;
+        final Optional<ByteArray> existingUserHandle = getCredentialRepository().getUserHandleForUsername(username);
+        
+        if (existingUserHandle.isPresent()) {
+            final byte[] handleAsBytes = existingUserHandle.get().getBytes();
+            assert handleAsBytes != null;
+            context.setUserHandle(handleAsBytes); 
+        } else {            
+            final byte[] userHandle = userHandleGeneratorStrategy.apply(profileRequestContext);
+            if (userHandle == null) {
+                log.trace("{} Generated UserHandle was null", getLogPrefix());
+                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+                return;
+            }
+            log.trace("{} Generated UserHandle '{}'",getLogPrefix(),userHandle);
+            context.setUserHandle(userHandle);   
         }
-        log.trace("{} Generated UserHandle '{}'",getLogPrefix(),userHandle);
-        context.setUserHandle(userHandle);   
              
     }    
     

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


More information about the commits mailing list