How to authenticate passkeys
Passkey authentication works through a collaborative process between the front-end and backend.
We would like to thank Apple for their WWDC session and their in-person lab as well as Nick Steele for having taken the time to help us understand the process and what is required to make them work securely.
Passkeys are passwordless passwords, are far more secure than passwords and in the future will replace passwords entirely. For more information on what they are please consult the link below.
There are two parts to passkeys: Registration (account creation) and Authentication (login) and carry the option for Attestation at both parts, which involves validating that a device actually created the passkey.
Attestation is an optional but strongly recommended security measure but is not required to make passkeys work for your website or app. For a proof of concept on how to create passkeys without attestation for a website in React or GatsbyJS, please use the link below (Github repository included).
The following tutorial covers Authentication. If you are curious on how registration works or a step by step proof of concept on registering and authenticating passkeys in ReactJS please use the links below.
Authenticating a passkey is outlined below and involves using the web Navigator to gather a credential to validate.
It is important that all the authentication and decoding of data happens on the backend to avoid a security vulnerability.
For sample projects or a proof of concept for logging in, in React or GatsbyJS please use the links below.
The authentication process should work as follows:
Step One: User requests a login | Front-End
A user goes to your website and wishes to login to their account. They complete the login form and request access to their account.
At this point your front-end sends an API request to your backend to gather the details for an account with the details that the user gave. Within this API request, we are required to include:
- A unique identifier (often an email)
Step Two: Validate account exists | Backend
This part involves making sure that the account exists and returning login specific details.
If the unique identifier (i.e. email) does not exist, we return that this account does not exist and end the authentication flow at this step.
If the account exists, you would return the following details in an API call:
- The challenge buffer string for the requested account.
Step Three: Login | Front-End
This step assumes that the account exists (i.e. this was an account available on our backend).
You would then use the challenge buffer from the API response to allow the user to login with a passkey.
From the data returned from the passkey login event, you would then gather the clientDataJSON and the userHandle and send it in an API call to the backend for verification.
For a proof of concept use the link below.
Step Four: Verification | Backend
The backend would then decode the clientDataJSON and the userHandle from the API call to gather the challenge (from the clientDataJSON) and userId (from the userHandle). You would then verify that the userId and challenge matches those for the account tied to the email that was used to login within our server.
If it matches, we return that the login succeeded, if it doesn't the login failed.
Attest Authentication Data (OPTIONAL BUT RECOMMENDED)
In step 3, after logging in, you would send the client data and attestation object created from the login to the backend for attestation.
This process is optional but strongly recommended and is yet to be covered by delasign and we will release a tutorial in due time. For those seeking to cover this step, Nick Steele recommends that you use the a WebAuthn sample project or a FIDO npm library to complete the attestation as creating your own process without expert supervision is extremely challenging.
The API call that's returned from this request would confirm if a login was valid or not.
We are actively looking for feedback on how to improve this resource. Please send us a note to firstname.lastname@example.org with any thoughts or feedback you may have.