Hail WebAuthn!

Hail WebAuthn!

A new form of authentication that is both easy to use and very secure.

Phishing is the #1 security problem on the web: 81% of hacking-related account breaches last year leveraged weak or stolen passwords. The industry’s collective response to this problem has been multi-factor authentication, but implementations are fragmented and most still don’t adequately address phishing. We have been working with the FIDO Alliance since 2013 and, more recently, with the W3C to implement a standardized phishing-resistant protocol that can be used by any Web application.

I recently started doing a course on privacy and security in distributed systems. As part of the coursework, we started researching on better ways to provide security to web applications.

I stumbled across WebAuthn during the process. This approach is quite similar to how FIDO / Yubikey based USB devices work. In addition, WebAuthn identifies the platform based on the User Agent and triggers the platform credential manager to use things like TouchID, Windows Hello or regular FIDO USB drives which are much more easy to work with.

WebAuthn extends CredentialManager API to add a new credential type called PublicKeyCredential. WebAuthn abstracts the communication between the browser and authenticator and allows a user to:

  1. Create and register a public key credential for a website.
  2. Authenticate a website by proving possession of the corresponding private key.

Enough with all the theory, let’s get practical.

Creating a key pair and registering a user

When a user wants to register a credential to a website (referred to by WebAuthn as the “relying party”):

  1. The relying party generates a challenge.
  2. The relying party asks the browser, through the Credential Manager API, to generate a new credential for the relying party, specifying device capabilities, e.g., whether the device provides its own user authentication (with biometrics, etc.).
  3. After the authenticator obtains user consent, the authenticator generates a key pair and returns the public key and optional signed attestation to the website.
  4. The web app forwards the public key to the server.
  5. The server stores the public key, coupled with the user identity, to remember the credential for future authentications.
let credential = await navigator.credentials.create({ publicKey: {  
  challenge: new Uint8Array([117, 61, 252, 231, 191, 241, ...]),  
  rp: { id: "example.com", name: "Example Corporation" },  
  user: {  
    id: new Uint8Array([79, 252, 83, 72, 214, 7, 89, 26]),  
    name: "johndoe",  
    displayName: "John Doe"  
  pubKeyCredParams: [ {type: "public-key", alg: -7} ]  

Authenticating a user

When a website needs to obtain proof that it is interacting with the correct user:

  1. The relying party generates a challenge and supplies the browser with a list of credentials that are registered to the user. It can also indicate where to look for the credential, e.g., on a local built-in authenticator, or on an external one over USB, BLE, etc.
  2. The browser asks the authenticator to sign the challenge.
  3. If the authenticator contains one of the given credentials, the authenticator returns a signed assertion to the web app after receiving user consent.
  4. The web app forwards the signed assertion to the server for the relying party to verify.
  5. Once verified by the server, the authentication flow is considered successful.
let credential = await navigator.credentials.get({ publicKey: {  
  challenge: new Uint8Array([139, 66, 181, 87, 7, 203, ...]),  
  rpId: "acme.com",  
  allowCredentials: [{  
    type: "public-key",  
    id: new Uint8Array([64, 66, 25, 78, 168, 226, 174, ...])  
  userVerification: "required",  

Try WebAuthn yourself at https://webauthndemo.appspot.com/.