Introduction

This article continues from Introduction to FIDO.

I’ll cover the WebAuthn API in more detail and share my experiences from working with the API. I’m also introducing an interactive WebAuthn API testing tool you can use to explore the API yourself.

The end of this article covers a couple of use cases on a higher level.

WebAuthn API

The API is specified by Web Authentication: An API for accessing Public Key Credentials published by W3C. The status of this specification was recently (March 2019) updated to W3C Recommendation, which means the specification is complete and no major changes are expected.

On the surface the API looks quite simple with only three relevant methods

It turns out the WebAuthn API is an extension of another API: Credential Management Level 1. In practice, this doesn’t matter much but some of the terminology and naming conventions are inherited from this parent specification.

Registration

Registration request syntax

The registration request is specified by PublicKeyCredentialCreationOptions. The top-level members of this structure are:

  • rp.id, rp.name, rp.icon
    Information about the relying-party or the application where rp.id by default has value of location.host
    It is possible to set rp.id to the value of the parent domain to register credentials for a common domain – for example, if the application is on login.example.com then rp.id can be example.com
  • user.id, user.name, user.displayName, user.icon
    Information about the user registering
    The user.id value appears as userHandle member of Authentication Response
    User information helps the username-less use case, where this information helps them choose from multiple credentials
  • challenge
    Cryptographic challenge with enough randomness to allow detecting replay
  • pubKeyCredParams
    List of cryptographic algorithms supported by the application
    COSE identifiers are used, where -7 is ES256 and -257 is RS256
  • excludeCredentials
    Lists credentials already registered for the user. The intent is to prevent registering multiple credentials on a single authenticator. Windows Hello, for example, appears to replace existing credentials.
  • authenticatorSelection
    Controls what types of authenticators are accepted for registration.
    If more than one authenticator match this criteria then the user is presented with a menu to choose from.

    • authenticatorAttachment
      Select either a platform or portable authenticator
    • requireResidentKey
      Select an authenticator with resident credential storage
    • userVerification
      Select an authenticator that implements user verification
  • attestation
    Request authenticator attestation. In this article I’m not covering processing attestations.

The isUserVerifyingPlatformAuthententicator method can be used to detect if a platform authenticator that implements user verification exists on the user’s device. This allows the application to improve user experience by only displaying options in the user interface that are sure to exist.

Registration request example

An example registration request with a minimal set of parameters

{
  "publicKey": {
    "rp": {
      "name": "https://psteniusubi.github.io"
    },
    "user": {
      "name": "hello@example.com",
      "displayName": "Hello Example",
      "id": "aGVsbG9AZXhhbXBsZS5jb20"
    },
    "challenge": "yGvVFCug1QLmaW4OnIYw7JONQP7XDSDNZ3SELT0PBhI",
    "pubKeyCredParams": [
      {
        "type": "public-key",
        "alg": -7
      },
      {
        "type": "public-key",
        "alg": -257
      }
    ]
  }
}

Registration response syntax

The registration response is specified by PublicKeyCredential and AuthenticatorAttestationResponse. Response processing rules are specified by Registering a New Credential.

The response is fairly complex with multiple nested data structures using various text and binary encodings such as JSON, CBOR, COSE and even ASN.1.

The top-level members of PublicKeyCredential are

  • type = “public-key”
    Identifies the response as PublicKeyCredential
  • id, rawId
    Credential identifier (same value, two different encodings)
  • response
    Contains an AuthenticatorAttestationResponse object

The top-level members of AuthenticatorAttestationResponse are

  • clientDataJSON
    JSON encoded parameters from registration request. Allows correlating request and response
  • attestationObject
    CBOR encoded attestation statement and authenticator data

Contents of clientDataJSON

  • type = “webauthn.create”
    Identifies this as the response to the registration request
  • challenge
    From the registration request, for correlating messages and to detect replay
  • origin
    Value of location.origin on the web page where request originated
  • tokenBinding
    Optional indication of token binding support

Contents of attestationObject

  • fmt = “none”
    Format of attestation statement. “none” if attestation was not requested
  • attStmt
    Attestation statement. Empty if attestation was not requested
  • authData
    Binary encoded authenticator data

Contents of authData

  • rpIdHash
    SHA 256 hash of rp.id parameter from request
  • flags
    Bit flags indicating authenticator capabilities (user present, user verified) and contents of authData structure (credential data and/or extension present)
  • signCount
    Signature counter. Always 0 in response to registration
  • attestedCredentialData
    • aaguid
      128 bit value, identifies authenticator type. May be zero
      Windows Hello aaguid values https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/windows-integration/web-authentication
    • credentialId
      Credential identifier, same as id and rawId top-level parameters
    • credentialPublicKey
      COSE encoded EC or RSA public key

Registration response example

An example registration response

{
  "id": "9Y_H6NruEEvScYq3NeRJP-28PL9omSMkfC6s2EuTmj4",
  "type": "public-key",
  "rawId": "9Y_H6NruEEvScYq3NeRJP-28PL9omSMkfC6s2EuTmj4",
  "response": {
    "clientDataJSON": "ew0KCSJ0eXBlIiA6ICJ3ZWJhdXRobi5jcmVhdGUiLA0KCSJjaGFsbGVuZ2UiIDogInlHdlZGQ3VnMVFMbWFXNE9uSVl3N0pPTlFQN1hEU0ROWjNTRUxUMFBCaEkiLA0KCSJvcmlnaW4iIDogImh0dHBzOi8vcHN0ZW5pdXN1YmkuZ2l0aHViLmlvIiwNCgkidG9rZW5CaW5kaW5nIiA6IA0KCXsNCgkJInN0YXR1cyIgOiAic3VwcG9ydGVkIg0KCX0NCn0",
    "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVkBZyzXk8-gtBjLvw6oUiKGB9npezM9TbCkEBd2ava7jHVRRQAAAABgKLAXsdRMArSzr82vyWuyACD1j8fo2u4QS9Jxirc15Ek_7bw8v2iZIyR8LqzYS5OaPqQBAwM5AQAgWQEA0Y2rM1KvYnVmRX-MHi2c3kdBNssnaTg_TzfoBRrJjw3PnV4-hMvPRXnDKxy4W4Ka2UZrfrJ9guTndbpcK6yVcBLNQoDEhTNrf2OuSUaUKJ9dw1GPFrzCkWgq_C8ZdkFCWbNylHj1w3E0yJMOAHnFX6D-9WyT6fLEXhTuJYE8v4ugLO_YHRIPyxSahophXlczhkGeJHg97unr4xKJjC2ssuAnpM2nvYTd2yV_VOe4x3Vrpbj40eItEt0s5hCuA5ivxI4VRo07MbWWvR910a13d_h-p-MJA5DBbHCF87Ee_N6z4UKWtE8aepI1purzb0_iVjKnt_cyTTtIJHcCKu5qvSFDAQAB"
  }
}

Decoded clientDataJSON

{
  "type": "webauthn.create",
  "challenge": "yGvVFCug1QLmaW4OnIYw7JONQP7XDSDNZ3SELT0PBhI",
  "origin": "https://psteniusubi.github.io",
  "tokenBinding": {
    "status": "supported"
  }
}

Decoded attestationObject

{
  "fmt": "none",
  "attStmt": {},
  "authData": "LNeTz6C0GMu_DqhSIoYH2el7Mz1NsKQQF3Zq9ruMdVFFAAAAAGAosBex1EwCtLOvza_Ja7IAIPWPx-ja7hBL0nGKtzXkST_tvDy_aJkjJHwurNhLk5o-pAEDAzkBACBZAQDRjaszUq9idWZFf4weLZzeR0E2yydpOD9PN-gFGsmPDc-dXj6Ey89FecMrHLhbgprZRmt-sn2C5Od1ulwrrJVwEs1CgMSFM2t_Y65JRpQon13DUY8WvMKRaCr8Lxl2QUJZs3KUePXDcTTIkw4AecVfoP71bJPp8sReFO4lgTy_i6As79gdEg_LFJqGimFeVzOGQZ4keD3u6evjEomMLayy4Cekzae9hN3bJX9U57jHdWuluPjR4i0S3SzmEK4DmK_EjhVGjTsxtZa9H3XRrXd3-H6n4wkDkMFscIXzsR783rPhQpa0Txp6kjWm6vNvT-JWMqe39zJNO0gkdwIq7mq9IUMBAAE"
}

Decoded authData

{
  "rpIdHash": "LNeTz6C0GMu_DqhSIoYH2el7Mz1NsKQQF3Zq9ruMdVE",
  "flags": {
    "value": 69,
    "up": true,
    "uv": true,
    "at": true,
    "ed": false
  },
  "signCount": 0,
  "attestedCredentialData": {
    "aaguid": "YCiwF7HUTAK0s6_Nr8lrsg",
    "credentialId": "9Y_H6NruEEvScYq3NeRJP-28PL9omSMkfC6s2EuTmj4",
    "credentialPublicKey": "pAEDAzkBACBZAQDRjaszUq9idWZFf4weLZzeR0E2yydpOD9PN-gFGsmPDc-dXj6Ey89FecMrHLhbgprZRmt-sn2C5Od1ulwrrJVwEs1CgMSFM2t_Y65JRpQon13DUY8WvMKRaCr8Lxl2QUJZs3KUePXDcTTIkw4AecVfoP71bJPp8sReFO4lgTy_i6As79gdEg_LFJqGimFeVzOGQZ4keD3u6evjEomMLayy4Cekzae9hN3bJX9U57jHdWuluPjR4i0S3SzmEK4DmK_EjhVGjTsxtZa9H3XRrXd3-H6n4wkDkMFscIXzsR783rPhQpa0Txp6kjWm6vNvT-JWMqe39zJNO0gkdwIq7mq9IUMBAAE"
  }
}

Decoded credentialPublicKey

{
  "kty": "RSA",
  "alg": "RS256",
  "n": "0Y2rM1KvYnVmRX-MHi2c3kdBNssnaTg_TzfoBRrJjw3PnV4-hMvPRXnDKxy4W4Ka2UZrfrJ9guTndbpcK6yVcBLNQoDEhTNrf2OuSUaUKJ9dw1GPFrzCkWgq_C8ZdkFCWbNylHj1w3E0yJMOAHnFX6D-9WyT6fLEXhTuJYE8v4ugLO_YHRIPyxSahophXlczhkGeJHg97unr4xKJjC2ssuAnpM2nvYTd2yV_VOe4x3Vrpbj40eItEt0s5hCuA5ivxI4VRo07MbWWvR910a13d_h-p-MJA5DBbHCF87Ee_N6z4UKWtE8aepI1purzb0_iVjKnt_cyTTtIJHcCKu5qvQ",
  "e": "AQAB"
}

Authentication

Authentication request syntax

The authentication request is specified by PublicKeyCredentialRequestOptions. The top-level members of this structure are

  • challenge
    Cryptograhic challenge with enough randomness to allow detecting replay
  • rpId
    Information about the relying-party. Must match the value of rp.id from registration request
  • allowCredentials
    List of credentials registered for the user. Not set for username-less use case. Must be used with authenticators that implement client side storage model.
    If more than one authenticator match this criteria then user is presented with a menu to choose from.
  • userVerification
    What level of user verification is required for authentication

Authentication request example

An example authentication request with a minimal set of parameters

{
  "publicKey": {
    "challenge": "3RyGpNPHTqDdJ3LSEhSv_pWydPcNrrSAXWuRta_9K8g"
  }
}

Authentication response syntax

The registration response is specified by PublicKeyCredential and AuthenticatorAssertionResponse. Response processing rules are specified by Verifying an Authentication Assertion.

The top-level members of PublicKeyCredential are

  • type = “public-key”
    Identifies the response as PublicKeyCredential
  • id, rawId
    Credential identifeier (same value, two different encodings)
  • response
    Contains an AuthenticatorAssertionResponse object

The top-level members of AuthenticatorAssertionResponse are

  • clientDataJSON
    JSON encoded parameters from authentication request. Allows correlating request and response.
  • authenticatorData
    CBOR encoded authenticator data
  • signature
    Digital signature of clientDataJSON and authenticatorData. Verified with credentialPublicKey from registration response.
    For EC algorithm the signature value is ASN.1 encoded
  • userHandle
    user.id value that was used while registering this credentials. May be zero

Contents of clientDataJSON

  • type = “webauthn.get”
    Identifies this as response to authentication request
  • challenge
    From registration request, for correlating messages and to detect replay
  • origin
    Value of location.origin on the web page where request originated
  • tokenBinding
    Optional indication of token binding support

Contents of authenticatorData

  • rpIdHash
    SHA 256 hash of rpId parameter from request
  • flags
    Bit flags indicating authenticator capabilities (user present, user verified) and contents of authData structure (extension present)
  • signCount
    Signature counter. Application may use this field to detect duplicated credentials

Authentication response example

{
  "id": "9Y_H6NruEEvScYq3NeRJP-28PL9omSMkfC6s2EuTmj4",
  "type": "public-key",
  "rawId": "9Y_H6NruEEvScYq3NeRJP-28PL9omSMkfC6s2EuTmj4",
  "response": {
    "clientDataJSON": "ew0KCSJ0eXBlIiA6ICJ3ZWJhdXRobi5nZXQiLA0KCSJjaGFsbGVuZ2UiIDogIjNSeUdwTlBIVHFEZEozTFNFaFN2X3BXeWRQY05yclNBWFd1UnRhXzlLOGciLA0KCSJvcmlnaW4iIDogImh0dHBzOi8vcHN0ZW5pdXN1YmkuZ2l0aHViLmlvIiwNCgkidG9rZW5CaW5kaW5nIiA6IA0KCXsNCgkJInN0YXR1cyIgOiAic3VwcG9ydGVkIg0KCX0NCn0",
    "authenticatorData": "LNeTz6C0GMu_DqhSIoYH2el7Mz1NsKQQF3Zq9ruMdVEFAAAAAQ",
    "signature": "UYkZ_1nlyV8L5ByJ8yGhIjv8WSkfuFKG00pGwy_HFrDRo1FIbYPfMm_KZ01h0OhnHAEDlEuvvp-DAoHNtaP2KAesP-MTKaw4qhTvuEqdQ4uTjAtrwwLg7OsgE32FSVWLPiMGbowFrPEZaGLYtv9zk5lYqCWbWpAwN20kWmBCDpub2hbB4VlhECcne0804-Bd7tDfc89qL0Uukfdf7zjiX6YAS5P6Bvk-gug4Gx8YatvQU7PpyKZvb0oNKHQxwej-iQqx9Nsaen4F9Xcg_XLNFH18uf3hNoIAqylVQjkZxsMLob7lQCKrP9aLYyjl2-IZO9i6ZiM8RNiZEy_DAK4DvA",
    "userHandle": "aGVsbG9AZXhhbXBsZS5jb20"
  }
}

Decoded clientDataJSON

{
  "type": "webauthn.get",
  "challenge": "3RyGpNPHTqDdJ3LSEhSv_pWydPcNrrSAXWuRta_9K8g",
  "origin": "https://psteniusubi.github.io",
  "tokenBinding": {
    "status": "supported"
  }
}

Decoded authenticatorData

{
  "rpIdHash": "LNeTz6C0GMu_DqhSIoYH2el7Mz1NsKQQF3Zq9ruMdVE",
  "flags": {
    "value": 5,
    "up": true,
    "uv": true,
    "at": false,
    "ed": false
  },
  "signCount": 1
}

WebAuthn testing tool

The testing tool is implemented as a browser-based application. This tool keeps registered credentials in the browser’s local storage. There are three different pages:

  • credential-create.html
    For the registration use case. Use drop-down menus to test with different parameters of registration request.
  • credential-get.html
    For the authentication use case. Use drop-down menus to test with different parameters of authentication request.
  • credential-edit.html
    To manage credentials saved by this tool in the browser’s local storage. It is possible to copy credentials across browsers and devices.

The testing tool source code is on GitHub https://github.com/psteniusubi/webauthn-tester.

Requirements

Windows

On Windows 10 version 1809 and later you need to activate Windows Hello by clicking here ms-settings:signinoptions. Your computer does not need any special hardware as it’s possible to activate Windows Hello with a software authenticator and PIN code protection.

Windows Hello

Android

On mobile phones with recent versions of Android, everything should be ready to run.

Instructions

Registration

Navigate to credential-create.html

Click credentials.create() button

screenshot

Authentication

Navigate to credential-get.html

Click credentials.get() button

screenshot

Note about Chrome and new Chromium based Edge

On a Chrome browser you may see this error

NotSupportedError: Resident credentials or empty 'allowCredentials' lists are not supported at this time.

To work around this you need to choose a previously registered credential from the allowCredentials list

screenshot

Registration use cases

Registering multiple authenticators

Registering multiple authenticators for an account is useful for many reasons. A user should be allowed to register a platform authenticator on each device the user uses. The user also needs to register one or more portable authenticators to allow registering new devices or as backup keys for account recovery purposes.

The process is complex and the application should guide the user to avoid mistakes. The application should not let users register the wrong type of authenticator or create multiple credentials on a single authenticator.

Registering a portable authenticator

If the user has not registered any authenticators, then the user needs to sign in with a password or create a new account.

If the user has an existing account then the user needs to sign in with a portable authenticator. For an existing account excludeCredentials is set to list of existing credentials to prevent re-using an existing authenticator.

The application guides the user to register a portable authenticator by setting authenticatorAttachment property.

In this example, the user has already registered another portable authenticator which is listed in excludeCredentials.

{
  "publicKey": {
    "rp": {
      "name": "https://psteniusubi.github.io"
    },
    "user": {
      "name": "hello@example.com",
      "displayName": "Hello Example",
      "id": "aGVsbG9AZXhhbXBsZS5jb20"
    },
    "challenge": "im0sLy6nVAl0loRnDbpZ2ES1W9cAAY7Xlqnw55ixNR4",
    "pubKeyCredParams": [
      {
        "type": "public-key",
        "alg": -7
      },
      {
        "type": "public-key",
        "alg": -257
      }
    ],
    "excludeCredentials": [
      {
        "id": "eAQuVb1k_vKF7MYFbmkfHMEuqlASg1Xz_fQaK-O2U5M",
        "type": "public-key"
      }
    ],
    "authenticatorSelection": {
      "authenticatorAttachment": "cross-platform"
    }
  }
}

Sign in with portable authenticator

To register a new device or to register a new portable authenticator, the user first authenticates with one of the registered portable authenticators. The application sets allowCredentials to list all registered portable authenticators.

{
  "publicKey": {
    "challenge": "iqBhlGkxkZWkZdkG5H6Q2C65DUfD18cYEXYPBXsEjcQ",
    "allowCredentials": [
      {
        "id": "eAQuVb1k_vKF7MYFbmkfHMEuqlASg1Xz_fQaK-O2U5M",
        "type": "public-key"
      }
    ]
  }
}

Registering a new device

The user first signs in with a portable authenticator, then the application guides the user to register a new platform authenticator that supports user verification. The application sets excludeCredentials to list of all authenticators to prevent re-using an existing authenticator, and also sets authenticatorAttachment and userVerification.

{
  "publicKey": {
    "rp": {
      "name": "https://psteniusubi.github.io"
    },
    "user": {
      "name": "hello@example.com",
      "displayName": "Hello Example",
      "id": "aGVsbG9AZXhhbXBsZS5jb20"
    },
    "challenge": "w9tfGIKwkvw_JnIVseHd6sKXatSQJ7tkPrGTNQOmczE",
    "pubKeyCredParams": [
      {
        "type": "public-key",
        "alg": -7
      },
      {
        "type": "public-key",
        "alg": -257
      }
    ],
    "excludeCredentials": [
      {
        "id": "eAQuVb1k_vKF7MYFbmkfHMEuqlASg1Xz_fQaK-O2U5M",
        "type": "public-key"
      }
    ],
    "authenticatorSelection": {
      "authenticatorAttachment": "platform",
      "userVerification": "required"
    }
  }
}

Authentication use cases

What authentication use case variations can you implement with WebAuthn? The difference in use cases is related to what information the user needs to provide and what authentication factors are considered.

The available factors are:

  • Something you know
    Password of your user account
    PIN code used to unlock FIDO authenticator
  • Something you have
    Laptop or mobile phone with a platform FIDO authenticator
    Cross-platform FIDO authenticator, such as a USB token
  • Something you are
    Biometric gesture, such as fingerprint scan, used to unlock a FIDO authenticator

An authentication use case based on a combination of any two factors is generally considered secure.

With WebAuthn the typical use cases are controlled with authentication request parameters allowCredentials and userVerification.

Universal 2nd factor (U2F)

U2F is probably more common and better known in context of FIDO 1.0. However this use case is still valid with FIDO 2.0 and WebAuthn.

With this use case you need to provide both your username and password to initially identify yourself, so the ‘something you know’ factor is the password of your user account. The second factor, ‘something you have’, is the FIDO authenticator.

The application asks for username and password, then asks to authenticate with an authenticator that tests for user presence.

The combination of authentication factors is something you know and something you have.

Authentication request parameters

  • allowCredentials
    Set to list of credentials bound to user account
  • userVerification = “discouraged”
    A single-factor FIDO authenticator is sufficient

Example

{
  "publicKey": {
    "challenge": "65KV2ArKNS5i2wEqqoxzkkD6500xDOK4xUA_wBktIsk",
    "allowCredentials": [
      {
        "id": "9Y_H6NruEEvScYq3NeRJP-28PL9omSMkfC6s2EuTmj4",
        "type": "public-key"
      }
    ],
    "userVerification": "discouraged"
  }
}

Password-less authentication

With this use case you need to provide your username but the password of your user account is not needed. Instead, a FIDO authenticator with user verification is used.

The application asks for username only, then asks to authenticate with an authenticator bound to the user account that requires user verification.

The combination of authentication factors is either ‘something you know’ and ‘something you have’ (PIN code protected authenticator), or ‘something you are’ and ‘something you have’ (Biometric authenticator).

Authentication request parameters

  • allowCredentials
    Set to list of credentials bound to user account
  • userVerification = “required”
    A two-factor FIDO authenticator is required

Example

{
  "publicKey": {
    "challenge": "g4WfiNib0VwcyMlGfYUX1E-Rv1stkMYqnyqhHJR6e28",
    "allowCredentials": [
      {
        "id": "9Y_H6NruEEvScYq3NeRJP-28PL9omSMkfC6s2EuTmj4",
        "type": "public-key"
      }
    ],
    "userVerification": "required"
  }
}

Username-less authentication

With this use case you don’t need to initially provide any information. The application does not ask for username or password, instead the application asks to authenticate with any authenticator present on the user’s device that requires user verification. If you have registered more than one FIDO credential with this application then you are presented with a menu to choose from, so you don’t need to type in your username but get to choose from a menu.

screenshot
Windows Hello credential selection menu

This is a slight extension of the Password-less use case and only works with authenticators with resident keys, where the credential is stored embedded in the authenticator.

The combination of authentication factors is either ‘something you know’ and ‘something you have’ (PIN code protected authenticator), or ‘something you are’ and ‘something you have’ (Biometric authenticator).

Authentication request parameters

  • allowCredentials
    Not set because the user is not initially identified
  • userVerification = “required”
    A two-factor FIDO authenticator is required

Example

{
  "publicKey": {
    "challenge": "TUEY64516YPw2KYYkKmnJ-SA3hi3lCwvL5RL1Lln6nI",
    "userVerification": "required"
  }
}

Findings

Terminology

The terminology around FIDO and WebAuthn can be a bit confusing.

  • U2F – Universal 2nd Factor
  • 2FA – Two Factor Authentication
  • MFA – Multi Factor Authentication
  • 2SV – Two Step Verification
    (All of these three-letter acronyms are basically the same thing: authentication with more than one factor. I prefer MFA as it also stands for authentication with all three factors.)
  • Security Key, Security Token or Security Fob?
    What is the name of that tiny USB connector size thing that looks like a memory stick, but holds your credentials?
  • Password-less and Username-less
    Names for slight variations of MFA.
  • FIDO 1, FIDO 2 and WebAuthn
    FIDO 1 and FIDO 2 are quite different so there is a reason to separate. Hopefully FIDO (with no number) will soon be understood as FIDO 2 and WebAuthn.

security keys

Different shapes of security keys

Platform support

Many platforms and browsers have already implemented support. The remaining are quickly catching up. Things are moving fast so check the latest status from platform vendors.

Windows

The Windows 1809 update added WebAuthn and Windows Hello to the Edge Browser. The big surprise is that the next update in Windows 1903 adds Windows Hello to Chrome and Firefox too.

screenshot
Edge on Windows 1809

Chrome and Firefox

Before Windows 1903 these browsers only supported cross-platform authenticators, for example USB connected tokens.

USB connected tokens

Chrome on Windows before 1903

screenshot
Chrome on Windows 1903

Android

Chrome on Android has had support for WebAuthn since last year.

Google recently announced that a Bluetooth connected Android device will soon work as a cross-platform authenticator for Chrome on Windows.

OS X and iOS

Apple has released preview versions of Safari with WebAuthn.

Chrome has support for WebAuthn and Touch ID on OS X.

Conclusions

Is WebAuthn ready for the general public? The specification is complete. With Safari browser support in the pipeline, platform support is becoming available to most users. The recent announcement from Google, about enabling Android phones as WebAuthn portable authenticators, will increase access to convenient authenticators even further.

Can WebAuthn replace your password? That depends on what we expect ‘replace password’ to mean. Your application could simply use WebAuthn authentication as the most frequent and convenient authentication method, but still keep passwords as a fallback. Or you can aim at significantly improving security and remove passwords completely.

The first alternative does not completely remove passwords from user accounts but allows using a WebAuthn authenticator as an option to the account password. During sign in, the application allows users to choose between username-less, password-less or traditional username and password authentication methods. This improves the sign in experience but does not improve security much, as the risky password option remains available as a fallback.

Windows sign in
Microsoft has enabled username-less sign in as an option on Windows Live.

If you want to improve security and completely remove passwords from user accounts then your application also needs to consider account recovery and registration of new devices. You cannot fall back to a significantly less secure method such as a password. To allow account recovery in case of a lost or damaged authenticator, you need to make sure your users register at least two different physical authenticators.

screenshot
The Google Advanced Protection Program requires two or more authenticators.

Probably the simplest integration is to not try to replace the password at all, but implement the U2F use case and add WebAuthn as one more option for second factor authentication. WebAuthn could be more convenient than some other options such as mobile authenticator apps or passwords sent as text messages.

2 step verification screenshot
Google allows you to add a portable WebAuthn authenticator as a second factor to your normal Google account.

When considering implementing WebAuthn it is probably a good time to again re-consider whether to build or buy a IAM solution. WebAuthn is a new and important technology in the IAM space that improves both usability and overall security, but comes with its own set of complexities.