What is WebAuthn: Logging in with Face ID and Touch ID on the web
Tags: javascript, react, authentication, tutorial
Published: 2020-07-28 17:02:27

---
title: What is WebAuthn: Logging in with Face ID and Touch ID on the web
published: true
date: 2020-07-28 07:13:42 UTC
tags: Javascript, ReactJs, Authentication, Tutorial
canonical_url: https://blog.cotter.app/what-is-webauthn-logging-in-with-face-id-and-touch-id-on-the-web/
---

_Enable TouchID and Windows Hello authentication to your website. Introducing WebAuthn: how it works and how to implement it._

 ![What is WebAuthn: Logging in with Face ID and Touch ID on the web](https://blog.cotter.app/content/images/2020/07/Cover-4.png)

### What is WebAuthn?
The Web Authentication API is an authentication [specification](https://w3c.github.io/webauthn/) that allows Websites to authenticate users with built-in authenticators like Apple's TouchID and Windows Hello, or using security keys like Yubikey.

![What is WebAuthn: Logging in with Face ID and Touch ID on the web](https://blog.cotter.app/content/images/2020/07/image-11.png)<figcaption>WebAuthn Login with Cotter</figcaption>

**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.

![What is WebAuthn: Logging in with Face ID and Touch ID on the web](https://blog.cotter.app/content/images/2020/07/Registration-2.png)<figcaption>WebAuthn Registration Flow</figcaption>

### 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.

![What is WebAuthn: Logging in with Face ID and Touch ID on the web](https://blog.cotter.app/content/images/2020/07/Login-3.png)<figcaption>WebAuthn Login Flow</figcaption>

## 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/](https://cotter.herokuapp.com/)

1. Make sure you're using **Google Chrome** on a laptop that supports TouchID/Windows Hello.
2. **Registration:** If this is your first time logging in, you'll be prompted to enable TouchID or Windows Hello after your email is verified.
3. **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

1. Import Cotter
2. Call `signInWithWebAuthnOrLink` to use WebAuthn with Magic Link as the fallback method, followed by `showEmailForm` or `showPhoneForm`, and get the response as a promise.
3. Setup a `<div>` with `id="cotter-form-container"` that will contain the form.

```react
import React, { useEffect, useState } from "react";
import Cotter from "cotter"; // 1️⃣ Import Cotter

function App() {
  const [payload, setpayload] = useState(null);

  // 2️⃣ Initialize and show the form
  useEffect(() => {
    var cotter = new Cotter(API_KEY_ID); // 👈 Specify your API KEY ID here
    cotter
      .signInWithWebAuthnOrLink() // 👈 sign in using WebAuthn
      .showEmailForm()
      .then(response => {
        setpayload(response); // show the response in our state
      })
      .catch(err => console.log(err));
  }, []);

  return (
    <div>
      {/* 3️⃣ Put a <div> that will contain the form */}
      <div id="cotter-form-container" style={{ width: 300, height: 300 }} />
      
      <pre>{JSON.stringify(payload, null, 4)}</pre>
    </div>
  );
}

export default App;
```
<figcaption>App.js</figcaption>

​You'll need an `API_KEY_ID` , [create a project, and copy the API KEY from the dashboard.](https://dev.cotter.app/)

#### 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](https://developer.apple.com/videos/play/wwdc2020/10670/).

### 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).

```react
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 is `https://login.example.com:1337`, then the `id` is `login.example.com` or `example.com`, but not `m.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](https://www.iana.org/assignments/cose/cose.xhtml#algorithms) 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.

```react
// 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:

```react
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](https://w3c.github.io/webauthn/#sctn-registering-a-new-credential). 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.

```react
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

```react
// 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:

```react
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](https://w3c.github.io/webauthn/#sctn-verifying-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](https://fidoalliance.org/fido2/fido2-web-authentication-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](https://www.cotter.app/) – we are building lightweight, fast, and passwordless login solution for websites and mobile apps.

If you want to implement WebAuthn, our documentation might help:

- [React Quickstart – Login with WebAuthn](https://docs.cotter.app/quickstart-guides/react-webauthn)
- [WebAuthn SDK Reference](https://docs.cotter.app/sdk-reference/web/sign-in-with-webauthn)

* * *

### References

We referred to these incredibly helpful articles to write this post:

- [WebAuthn Guide](https://webauthn.guide/)
- [WebAuthn Specs from W3C](https://w3c.github.io/webauthn/)

* * *

### Questions & Feedback

If you need help or have any feedback, ping us on [Cotter's Slack Channel](https://join.slack.com/t/askcotter/shared_invite/zt-dxzf311g-5Mp3~odZNB2DwYaxIJ1dJA)! 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](https://dev.cotter.app/) and [check out our documentation](https://docs.cotter.app/).