MasterKale / SimpleWebAuthn

WebAuthn, Simplified. A collection of TypeScript-first libraries for simpler WebAuthn integration. Supports modern browsers, Node, Deno, and more.
https://simplewebauthn.dev
MIT License
1.63k stars 137 forks source link

How to use usernameless and passwordless at the same time #96

Closed akanass closed 3 years ago

akanass commented 3 years ago

As described in the Yubico developer Guide we can use User Handle to allow usernameless and passwordless authentication.

How to do it with this library and initiate attestation and assertion phases without a username because all documentation describes an associated user passed in parameter.

Thanks for your help.

akanass commented 3 years ago

I think I have my answer :)

If we don't give a username during assertion phase, we can't retrieve authenticators so the client will ask the user which one he wants to use but to validate it correctly after, we have to store User Handle and use it like describe in the documentation.

This validation should be added inside assertion verification phase because for now only the string type is checked for this attribute and User Handle value has to be stored inside AuthenticatorDevice to be validated.

And to store User Handle we have to save it during attestation phase because User Handle is the user.id field. This should be a stable, unique identifier for the user - equivalent to a username. However due to privacy considerations it is recommended to set the user handle to a random byte array rather than a username and not using the user primary key store in the db.

akanass commented 3 years ago

https://developers.yubico.com/WebAuthn/WebAuthn_Developer_Guide/Resident_Keys.html

akanass commented 3 years ago

If we want to authenticate an user without his username we have to set residentKey to required as describe in the specification and the user will be find in the DB with the User Handle value send by the authenticator.

Else, we have to ask him his username, retrieve authenticators associated to it, asking for user verification required and then validating User Handle to be sure all is good

MasterKale commented 3 years ago

passwordless.dev is @aseigler's project and my goto for playing around with Passwordless and Usernameless. I took a look at attestation options and distilled the differences down to this:

Passwordless

{
  "user": {
    "name": "namehere",
    "id": "bmFtZWhlcmU",
    "displayName": null
  },
  "authenticatorSelection": {
    "requireResidentKey": false,
    "userVerification": "preferred"
  }
}

Usernameless

{
  "user": {
    "name": " (Usernameless user created at 2/1/2021 5:53:46 PM)",
    "id": "IChVc2VybmFtZWxlc3MgdXNlciBjcmVhdGVkIGF0IDIvMS8yMDIxIDU6NTM6NDYgUE0p",
    "displayName": null
  },
  "authenticatorSelection": {
    "requireResidentKey": true,
    "userVerification": "preferred"
  }
}

(I've omitted common stuff like RP info, challenge, etc...)

So yes, as you called out from the Yubico guide one difference is requireResidentKey: true (or the new residentKey: "required" in L2 of the WebAuthn spec).

I started a "Cookbooks" section for the SimpleWebAuthn docs to document this exact thing: a collection of options devs can specify to support either of these flows. I'll experiment with this in the next few days and restart that effort to document these collections of settings.

akanass commented 3 years ago

@MasterKale thanks for your answer.

For both, we have to store User Handle like this we can validate the user when is authenticated with username or not by finding the user in the database with the User Handle value.

And user verification should be required for both to ensure the security.

That's why I'm asking you to add this field inside AuthenticatorDevice and validate it during verification phase.

MasterKale commented 3 years ago

Okay, after a few hours diving back into the spec I understand better now that for the sake of privacy userHandle (as user.id in attestation options) should always be a random 64-byte Buffer and never the user's actual internal DB ID.

This means that generateAttestationOptions() should be updated to generate that random userHandle every time and return it in attestation options. It'd then be the responsibility of the RP to take that userHandle, persist it until verification, then store it in the credentials table along with an fkey to the user's actual internal DB ID.

There's now additional load, outside of library functionality, to communicate all of this to people implementing this library who wish to take advantage of passwordless or usernameless flows...

That's why I'm asking you to add this field inside AuthenticatorDevice and validate it during verification phase.

I understand your request better now, too, and this makes perfect sense (with the clarification that "during verification phase" means during assertion verification). I'm going to model upcoming changes around this diagram on this specific page in Yubico's dev guide:

Screen Shot 2021-02-02 at 2 05 20 PM

akanass commented 3 years ago

Thanks @MasterKale and yes if we match the specific credentials data from Yubico I guess we are on the good way

akanass commented 3 years ago

UserHandle will allow us to find user in the database and the corresponding authenticator like this we can be sure that it was registered for this RP that's why we have to find AuthenticatorDevice inside the DB with UserHandle and validate all the fields save during attestation generation with the data received during attestation verification.

MasterKale commented 3 years ago

This issue is going to end up becoming a documentation issue, since PR #97 is adding additional return values from attestation that will be helpful in supporting passwordless and usernameless flows. Once that PR gets merged in work can begin on adding entries to the SimpleWebAuthn docs site containing "best practices" for setting up the RP to support these non-2FA WebAuthn flows.

MasterKale commented 3 years ago

I'm going to close this issue out for https://github.com/MasterKale/SimpleWebAuthn-homepage/issues/2.