passwordless-id / webauthn

Webauthn / passkeys helper library to make your life easier. Client side, server side and demo included.
https://webauthn.passwordless.id
MIT License
454 stars 53 forks source link

feat: added optional `userHandle` field to authentication response #41

Closed asgarovf closed 8 months ago

asgarovf commented 8 months ago

What

The authentication response does not contain the optional userHandle field.

Why

The original AssertionResponse object also contains userHandle field based on Webauthn spec. According to docs:

The authenticator MUST always return a userHandle if the allowCredentials option used in the authentication ceremony is empty, and MAY return one otherwise. However, in the current library the userHandle is never returned. userHandle field returns the userId provided in registration if the credential is discoverable (otherwise it is null) and we might need this value while authentication.

How

Add optional userHandle field to AuthenticationEncoded type and authentication response of webauthn client.

dagnelies commented 8 months ago

userHandle field returns the userId provided in registration if the credential is discoverable (otherwise it is null) and we might need this value while authentication.

Using this library, you can explicitly set it like that:

const registration = await client.register("MyUsername", challenge, {
  userHandle: "..."
})

The "user handle" is actually the "user id" during registration, it's just really confusing in the specs. In other words, you already know the handle when registering. This lib sets it for you and as far as I know, you actually never need it afterwards, so it isn't returned. Authentication does not need it, whether the credential is discoverable or not.

As far as I know, the only usage (which is not really in the spec) is to use this user handle to override an existing credential/passkey with a new one for that user/device (for example to change the display name). But that's already fairly advanced. Or is there something that I missed?

If it's useless, I would tend not to return it.

asgarovf commented 8 months ago

userHandle field returns the userId provided in registration if the credential is discoverable (otherwise it is null) and we might need this value while authentication.

Using this library, you can explicitly set it like that:

const registration = await client.register("MyUsername", challenge, {
  userHandle: "..."
})

The "user handle" is actually the "user id" during registration, it's just really confusing in the specs. In other words, you already know the handle when registering. This lib sets it for you and as far as I know, you actually never need it afterwards, so it isn't returned. Authentication does not need it, whether the credential is discoverable or not.

As far as I know, the only usage (which is not really in the spec) is to use this user handle to override an existing credential/passkey with a new one for that user/device (for example to change the display name). But that's already fairly advanced. Or is there something that I missed?

If it's useless, I would tend not to return it.

For example, in our use case we set the user id as public blockchain address to identify users. We need to get the returned userHandle (which is user id during registration ) value to query user in blockchain or in our db. Please refer to the flow below. We have overridden this function internally to make it work.

clave-auth

dagnelies commented 8 months ago

Aha... I missed the fact that the spec now says the userHandle should be returned during authentication. A year ago, it wasn't the case, and no authenticator returned it. I wonder how the situation is "in practice" by now. I'll take a closer look at this PR soon and expect it to be merged once docs and examples are updated.

asgarovf commented 8 months ago

Aha... I missed the fact that the spec now says the userHandle should be returned during authentication. A year ago, it wasn't the case, and no authenticator returned it. I wonder how the situation is "in practice" by now. I'll take a closer look at this PR soon and expect it to be merged once docs and examples are updated.

Sure, I will update the docs!

dagnelies commented 8 months ago

This webauthn protocol is really in constant evolution, with sometimes fundamental changes like synced keys instead of device-bound and browser behaviors that change each season.

...long ago this userHandle was a useless piece of junk ...now that it's returned during authentication, I wonder if renaming it into userId would be more sensible. In other words, going back to the original user:{id,name,displayName} and deprecate deprecate userHandle altogether. But let's leave that for "v2". ;)

dagnelies commented 8 months ago

should appear soon in v1.5.0

asgarovf commented 8 months ago

should appear soon in v1.5.0

Thanks! Agree on renaming to userId for simplicity in the future.