web-auth / webauthn-framework

FIDO-U2F / FIDO2 / Webauthn Framework
MIT License
421 stars 54 forks source link

How to implement registration when using Passkeys with email as user handle? #477

Closed nicodemuz closed 1 year ago

nicodemuz commented 1 year ago

Description

My goal is to implement Passkeys registration using an email address and name, as below: image

I already have implemented adding a Passkey to an existing account and login using it, which works fine. In this case, the public_key_credential_sources table contains the email address in the user_handle column.

Now that I'm implementing the registration form for new users, I'm unable to get the user_handle column to contain the registered email address. Instead it becomes a generated user ID that is likely done by this function:

    public function generateNextUserEntityId(): string
    {
        return Ulid::generate();
    }

I would like to save the email address in the user_handle column instead of a newly generated ID. How should I proceed to implement this?

My User entity has only id and email fields like below:

    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 180, unique: true)]
    #[Assert\Email]
    #[Assert\NotBlank]
    private string $email;
nicodemuz commented 1 year ago

I think I was able to get it working with the following hack (need to test further):

    public function generateNextUserEntityId(): string
    {
        $request = $this->requestStack->getCurrentRequest();

        $content = $request->getContent();

        $dto = $this->serializer->deserialize(
            $content,
            ServerPublicKeyCredentialCreationOptionsRequest::class,
            'json'
        );

        return $dto->username;
    }

A better approach is to probably write a custom guesser that doesn't use the generateNextUserEntityId() call.

Spomky commented 1 year ago

Hi,

Since 4.7.0, the method generateNextUserEntityId is deprecated. Your repository should implement Webauthn\Bundle\Repository\CanGenerateUserEntity. This interface has only one method public function generateUserEntity(?string $username, ?string $displayName): PublicKeyCredentialUserEntity;. You can simply return the object you want with the email as handle.

nicodemuz commented 1 year ago

Thanks for your help @Spomky.

If I replace the CanRegisterUserEntity interface with CanGenerateUserEntity interface, I get the following error in here: https://github.com/web-auth/webauthn-framework/blob/4.7.x/src/symfony/src/Security/Http/Authenticator/WebauthnAuthenticator.php#L240

Do I need to implement both interfaces?

image

nicodemuz commented 1 year ago

Ok, it seems to work if I implement both interfaces. Thanks!

Spomky commented 1 year ago

Hi,

Yes the interface should be kept for backward compatibility. Only part of it is deprecated (method generateNextUserEntityId).

Spomky commented 1 year ago

Also, please note that the use of an email address as user handle is not recommended (see https://www.w3.org/TR/webauthn-2/#sctn-user-handle-privacy). I recommended you generate a random unique immuable user handle you associate to each user.

Spomky commented 1 year ago

Closing as resolved. Feel free to open again if deemed necessary. Thanks.

github-actions[bot] commented 1 year ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.