Yubico / java-webauthn-server

Server-side Web Authentication library for Java https://www.w3.org/TR/webauthn/#rp-operations
Other
439 stars 137 forks source link

U2F UserHandle validation failure when a single user has multiple devices registered #313

Closed rksk closed 11 months ago

rksk commented 11 months ago

During the device registration, userHandle is populated with the userEntity.id as mentioned here. But it is possible to get userHandle as null during the authentication for FIDO U2F devices as mentioned here.

In the current implementation, it tires to get the userHandle from credentialRepository via getUserHandleForUsername() if the userHandle was null, ref. But when there are multiple devices registered under the same username, it the credentialRepository might return one of them. Now when credentialRepository.lookup() is invoked with credential id and userHandle from here, it won't match with any entry in the credentialRepository due to incorrect userHandle.

W3 says following here.

Identify the user being authenticated and verify that this user is the owner of the public key credential source credentialSource identified by credential.id:

  • If the user was identified before the authentication ceremony was initiated, e.g., via a username or cookie, verify that the identified user is the owner of credentialSource. If response.userHandle is present, let userHandle be its value. Verify that userHandle also maps to the same user.

  • If the user was not identified before the authentication ceremony was initiated, verify that response.userHandle is present, and that the user identified by this value is the owner of credentialSource.

But as far as I read the code, it seems we are always trying to validate both credentialId and userHandle.

It would be really great if you could help understanding whether this flow can be handled in a different way or needs to be fixed in the library.

emlun commented 11 months ago

Hi! This sounds like you are giving each credential a unique user handle, even credentials that belong to the same user. Is that the case? The user handle should be unique per user but shared between all credentials for that user.

rksk commented 11 months ago

Thanks a lot for the quick reply.

I believe we can use the same UserHandle during the registration if credentialRepository.getUserHandleForUsername() returns a value for the given user.

emlun commented 11 months ago

Sorry, I don't understand what you mean by that. Could you elaborate?

rksk commented 11 months ago

Sorry, let me add more information.

Currently, in our application, we assign a new random value for user handle for each registration request. Therefore, if a single user registers multiple devices, they will get assigned with different user handle values.

As per your suggestion, my understanding is that we can first search for existing records in the credentialRepository for the same user and use the existing user handle instead of a random value.

I just noticed that the same is included in this sample in the readme.

Optional<UserIdentity> findExistingUser(String username) { /* ... */ }

PublicKeyCredentialCreationOptions request = rp.startRegistration(
  StartRegistrationOptions.builder()
    .user(
        findExistingUser("alice")
            .orElseGet(() -> {
                byte[] userHandle = new byte[64];
                random.nextBytes(userHandle);
                return UserIdentity.builder()
                    .name("alice")
                    .displayName("Alice Hypothetical")
                    .id(new ByteArray(userHandle))
                    .build();
            })
    )
    .build());
emlun commented 11 months ago

Yes, that is how the WebAuthn API (not just this library) is intended to be used. And yes, setting different user handles for different credentials for the same user will cause issues like the one you're seeing.

rksk commented 11 months ago

Thank you very much for the quick help on this 🤝

emlun commented 11 months ago

Happy to help, I'm glad it worked out!