Introducing the WebAuthn Debugger

TL;DR We've written about one of the latest additions to the W3C specs, the web authentication API, before. The API requires many configuration objects that you may be seeing for the first time. To help you understand these configuration variables we've added a debugger to the webauthn.me website. It lets you play around with different configurations and test them on the fly.

The Web Authentication API has 2 methods. A create() method to register new credentials and a get() method to use previously created credentials.

Creating Credentials

Let's start with looking at the configuration for the create() method. Below you'll find some example code on how to use the navigator.credentials.create() method with some configuration in place. Let's break it down.

navigator.credentials.create({
 publicKey: {
   challenge: Uint8Array([1, 2, ... 3, 4]),
   rp: {
     id: 'webauthn.me',
     name: 'WebAuthn debugger'
   },
   user: {
     id: Uint8Array([1, 2, ... 3, 4]),
     name: 'Sam Bellen',
     displayName: 'Sambego'
   },
   pubKeyCredParams: [
     {
       type: "public-key",
       alg: -7
     }
   ],
   timeout: 15000,
   excludeCredentials: [{
       type: 'public-key',
       id: Uint8Array([4, 3, ... 2, 1]),
       transports: ['USB', 'NFC', 'BLE', 'internal']
   }],
   authenticatorSelection: {
       authenticatorAttachment: 'platform',
       requireResidentKey: true,
       userVerification: 'preferred'
   },
   attestation: 'direct',
 },
})
.then(console.log)
.catch(console.error);

Challenge

The challenge is a buffer of randomly generated bytes with a minimum of 16 bytes. This is generated on the server using a cryptographically secure random number generator. By generating the challenge on the server we can prevent "replay attacks". The authenticator will sign this along with other data.

Relying Party

This is the entity which is responsible for handling all things authentication, usually your authorization server or identity provider (IdP). The id must be the current domain or a subset of it. The name is used to describe the relying party.

User

The user object contains profile information about the user like its name and preferred display name. It also contains a user id which is again a buffer with byte values. To ensure secure operation, authentication and authorization decisions must be made based on this user id, not the name or display name. The user id can not contain information that can identify a user, like a username or an email.

Public Key Credential Parameters

This is a collection of accepted public key types. The algorithm (alg) is a number that references a key type in this list of COSE algorithms.

Timeout

Defines the maximum time in milliseconds the user has to complete the registration action. This can be touching their authenticator device, TouchID or any other method used to interact with an authenticator.

Excluded Credentials

You can use this if you wish to limit the creation of multiple credentials for the same account on a single authenticator. Your browser will throw an error if you try to create a new credential while one of the public keys in this collection already exists on the authenticator.

Authenticator Selection

You can limit the type of authenticator devices you allow to register new credentials with part of the configuration.

Authenticator Attachment

Only allow platform authenticators like TouchID or Windows Hello. You can also do the opposite, and only allow cross-platform authenticators like a Yubikey or a Google Titan Security Key.

Require Resident Key

When set to true, the private key is stored on the authenticator. This means that the user can login without entering a username. This can be done with the user.id property we’ve seen before. The relying party will create a user handle which is stored in the resident key on the authenticator when creating a new credential. When authenticating the authenticator will return the user handle, so the relying party can look up the user linked to this user handle.

User Verification

Use the user verification option to only allow or discourage authenticators that verify the user is performing the registration. By checking a fingerprint with TouchID or doing facial recognition with Windows Hello the authenticator can verify the user performing the registration.

Attestation

An attestation object is returned when completing the registration. With this parameter, you can specify if you want the attestation data from the authenticator as is (direct), or you're fine with anonymized (indirect) data.

When you successfully register a new credential, the create() method will return a credential object. This contains the id of the newly created credential in base64 and binary (rawID). We'll use this ID when authenticating a registered user with the second method specified by WebAuthn, navigator.credentials.get()

Authenticating a User with a Previously Registered Credential

So you've registered a new credential, and now you want to authenticate using that credential. Like the create() method, the get() method also accepts a configuration object. Let's have a look.

navigator.credentials.get({
 publicKey: {
   challenge: Uint8Array([1, 2, ... 3, 4]),
   timeout: 15000,
   rpId: 'webauthn.me',
   allowCredentials: [{
       type: 'public-key',
       id: Uint8Array([1, 2, ... 3, 4]),
       transports: ['USB', 'NFC', 'BLE']
   }],
   userVerification: 'preferred'
 }
})
.then(console.log)
.catch(console.error);

Challenge

Just as when registering a new credential, this is a buffer with cryptographically random bytes with a minimum length of 16 bits. Make sure you generate this challenge in a trusted environment, like a server.

Timeout

Again, the optional timeout sets the time the user has to complete the authentication action.

Relying Party Id

Also optional, you can specify the relying party. By default, the browser will use the current domain.

Allowed Credentials

This is a collection of credentials the server would like the user to use for authentication. The credentials are listed in descending order, meaning that the relying party prefers the first, then second, and so on. We will pass the id we got back when completing the registration here.

Wrapping up

WebAuthn is a new approach for a key-based registration and authentication on the web. By going through the most commonly used parts of the config you should be able to set up your WebAuthn process just right.

If you're still not sure which setting is best for you, visit the online interactive WebAuthn debugger, and see how changing some options can change the registration and authentication process and result. The debugger has an easy to use interface which let’s you change the configuration objects and test them. It’ll display the results after you’ve finished the WebAuthn request.

Do you still have some WebAuthn questions after reading this blog post, leave a comment below!