Enable TouchID and Windows Hello authentication to your website. Introducing WebAuthn: how it works and how to implement it.
What is WebAuthn?
The Web Authentication API is an authentication specification that allows Websites to authenticate users with built-in authenticators like Apple's TouchID and Windows Hello, or using security keys like Yubikey.
It utilizes public-key cryptography instead of passwords. When the user registers, a private-public key pair is generated for the account, and the private key is stored securely in the user's device, while the public key is sent to the server. The server can then ask the user's device to sign a challenge using the private key to authenticate the user.
Registration with WebAuthn
Usually, a website will ask the user to enter a username and password. With WebAuthn, the website will generate this public-private key pair and send the public key to the server and store the private key securely in the user's device.
Logging-in with WebAuthn
This is where websites usually check whether the user has provided the right username and password. With WebAuthn, the website will send a challenge and check if the browser can sign the challenge using the private key that's stored in the user's device.
Implementation
We've built a simple way to implement WebAuthn using Cotter that you can do in just a few minutes.
Try WebAuthn in Action.
We've made a simple site for you to try it out: https://cotter.herokuapp.com/
- Make sure you're using Google Chrome on a laptop that supports TouchID/Windows Hello.
- Registration: If this is your first time logging in, you'll be prompted to enable TouchID or Windows Hello after your email is verified.
- Login: Go to an incognito tab and open this URL again. You need to allow third-party cookies (eye icon on URL bar). Try logging in with the same email, and you'll be prompted to log in using WebAuthn.
Short Guide for Implementing WebAuthn with React
yarn add cotter
Implement Login with WebAuthn
- Import Cotter
- Call
signInWithWebAuthnOrLink
to use WebAuthn with Magic Link as the fallback method, followed byshowEmailForm
orshowPhoneForm
, and get the response as a promise. - Setup a
<div>
withid="cotter-form-container"
that will contain the form.
You'll need an API_KEY_ID
, create a project, and copy the API KEY from the dashboard.
What does signInWithWebAuthnOrLink
do?
- If the email is not recognized as a user , then Cotter will ask the user to verify their email by sending a MagicLink. After the user verified their email, Cotter's SDK will ask the user to enable WebAuthn login by registering the current device.
- If the email is a user that has WebAuthn set up , then Cotter will try to authenticate the user with WebAuthn. Alternatively, the user can choose to send a magic link to their email to login.
Implementation with vanilla JS
To learn more about WebAuthn, here's a more in-depth explanation about how to implement WebAuthn with purely JavaScript. Check out Apple's guide from WWDC20.
Registration
Step 1: Your site requests the server for registering WebAuthn.
Ask the user to enter some identifier (username, email, etc), then send the request to your server asking to register a new WebAuthn credential.
Step 2: The server specifies some options for creating the new keypair.
The server specify a PublicKeyCredentialCreationOptions
object that contains some required and optional fields for creating the new PublicKeyCredential
(our keypair).
const optionsFromServer = {
"challenge": "random_string", // need to convert to ArrayBuffer
"rp": { // my website info
"name": "My Website",
"id": "mywebsite.com"
},
"user": { // user info
"name": "anthony@email.com",
"displayName": "Anthony",
"id": "USER_ID_12345678910" // need to convert to ArrayBuffer
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7 // Accepted Algorithm
}
],
"authenticatorSelection": {
authenticatorAttachment: "platform",
},
"timeout": 60000 // in milliseconds
};
rp
: This is for specifying information about the relying party. The relying party is the website where the user is registering/logging-in into. If the user is registering to your website , then your website is the relying party.
-
id
: The host's domain name, without the scheme or port. For example, if the RP's origin ishttps://login.example.com:1337
, then theid
islogin.example.com
orexample.com
, but notm.login.example.com
.
pubKeyCredParams
: What public key types are acceptable to the server.
-
alg
: A number that describes what algorithm the server accepts, and is described in the COSE registry under COSE Algorithms. For example, -7 is for ES256 algorithm.
authenticatorSelection
: (Optional) Restrict authenticator to be either platform
or cross-platform
. Use platform
to allow authenticators like Windows Hello or TouchID. Use cross-platform
to allow authenticators like Yubikey.
Step 3: In the front-end, use the options to create a new keypair.
Using our creationOptions
, we can now tell the browser to generate a new keypair.
// make sure you've converted the strings to ArrayBuffer
// as mentioned above
const credential = await navigator.credentials.create({
publicKey: optionsFromServer
});
The credential
that is returned will look like below:
PublicKeyCredential {
id: 'ABCDESKa23taowh09w0eJG...',
rawId: ArrayBuffer(59),
response: AuthenticatorAttestationResponse {
clientDataJSON: ArrayBuffer(121),
attestationObject: ArrayBuffer(306),
},
type: 'public-key'
}
Step 4: Send the credential
to your server
First, you might need to convert the ArrayBuffer
s to either base64 encoded strings or just to string. You'll need to decode this in your server.
Follow the specifications to validate the credential
in your server. You should then store the Credential information to allow the user to login with this WebAuthn Credential.
Login
Step 1: Send a request to your server to login
This allows the server to send a challenge that your front-end needs to sign.
Step 2: The server sends a challenge and a list of WebAuthn credentials that the user can log in from.
The server specify a PublicKeyCredentialRequestOptions
object that contains the challenge to sign and a list of WebAuthn credentials that the user has registered previously.
const optionsFromServer = {
"challenge": "somerandomstring", // Need to convert to ArrayBuffer
"timeout": 60000,
"rpId": "mywebsite.com",
"allowCredentials": [
{
"type": "public-key",
"id": "AdPc7AjUmsefw37..." // Need to convert to ArrayBuffer
}
]
}
Step 3: The front-end signs the challenge
// make sure you've converted the strings to ArrayBuffer
// as mentioned above
const assertion = await navigator.credentials.get({
publicKey: optionsFromServer
});
The assertion
that is returned looks like this:
PublicKeyCredential {
id: 'ABCDESKa23taowh09w0eJG...', // The WebAuthn Credential ID
rawId: ArrayBuffer(59),
response: AuthenticatorAssertionResponse {
authenticatorData: ArrayBuffer(191),
clientDataJSON: ArrayBuffer(118),
signature: ArrayBuffer(70), // The signature that we need to verify
userHandle: ArrayBuffer(10),
},
type: 'public-key'
}
Step 4: Send the assertion
to your server and verify it
You might need to convert the ArrayBuffers to a string before sending it to the server. Follow the specifications on verifying the assertion.
When the assertion is verified, this means that the user has successfully logged in. This would be the place to generate your session tokens or set your cookies and return to the front-end.
A few things to think about:
If the user logs in with their laptop's TouchID, how do you allow them to log in from someone else's laptop?
It might be a bad user experience if they can only log in from their own laptop. A possible way is to use WebAuthn as an alternative , and always have a fallback login method (for example, using magic links or OTP).
Adding more than one WebAuthn credential for one account.
You might want to have a "Settings" page that allows your user to allow WebAuthn login from other devices. For example, they want to login with WebAuthn both from their laptop and their iPad.
The browser doesn't know which credentials you have saved in your server for a user. If your user already registered their laptop's WebAuthn credential, you need to tell the browser so that it doesn't create a new credential. Use the excludeCredentials
in the PublicKeyCredentialCreationOptions
.
Support for WebAuthn
Not all browsers support WebAuthn yet, but it's growing. Check out FIDO's website for a list of Browsers and Platforms that supports WebAuthn.
That's It!
This should cover the basics about registering and logging-in with WebAuthn, and help you implement it on your site. This post is written by the team at Cotter – we are building lightweight, fast, and passwordless login solution for websites and mobile apps.
If you want to implement WebAuthn, our documentation might help:
References
We referred to these incredibly helpful articles to write this post:
Questions & Feedback
If you need help or have any feedback, ping us on Cotter's Slack Channel! We're here to help.
Ready to use Cotter?
If you enjoyed this post and want to integrate Cotter into your website or app, you can create a free account and check out our documentation.
Top comments (1)
Did you discontinued the service?