WebAuthn Client Authentication

The RP can also request the user’s permission to perform an authentication operation with an existing credential. The authentication flow looks similar to the registration flow. The primary differences are:

  • Authentication does not require user information

  • Authentication creates an assertion signed by the previously generated private key that is associated with the relying party rather than signing with the attestation certificate.

webauthn-authentication-flow-01.svg?sanitize=true

Authentication Flow

0. The client initiates a request to authenticate on behalf of the user.

1. The RP builds an instance of the PublicKeyCredentialRequestOptions and returns it to the client. It contains the challenge and the allowCredentials field which contains a list of previously registered credentials that may be used to perform the authentication ceremony.

Example

Parsed PublicKeyCredentialRequestOptions

Note: In the WebAuthn API, all binary parameters (id, challenge etc.) are actually `UintArray`s and not Base64 strings. This example displays them as Base64 strings for readability

{
  "publicKey": {
    "allowCredentials": [
      {
        "id": "X9FrwMfmzj...",
        "type": "public-key"
      }
    ],
    "challenge": "kYhXBWX0HO5GstIS02yPJVhiZ0jZLH7PpC4tzJI-ZcA=",
    "rpId": "demo.yubico.com",
    "timeout": 30000,
    "userVerification": "discouraged"
  }
}

allowCredentials: list of public key credentials acceptable to the RP. Credentials can be omitted in username-less authentication scenario. An entry includes the type of the credential and credential id, and optionally a list of transports the client can use to find the credential.

challenge: contains a random value generated by the RP from the authenticator to sign as part of the authentication assertion.

rpId: relying party identifier claimed by the caller. This must exactly match the rp.id specified during registration.

timeout: the time, in milliseconds, that the caller is willing to wait for the call to complete.

userVerification: The default value is “preferred”. If “required”, the client will only use an authenticator capable of user verification. If "preferred", the client will use an authenticator capable of user verification if possible. If "discouraged", the authenticator is not asked to perform user verification but may do so at its discretion. See User Presence vs User Verification for guidance on setting this value for different use cases.

2. The JavaScript client calls navigator.credentials.get(). The browser validates the rp.id against the origin, hashes the clientData, and calls authenticatorGetAssertion method.

3. The authenticator finds a credential that matches the Relying Party ID and prompts the user to consent to the authentication. Assuming the steps are successful, the authenticator creates a new assertion by signing over the clientDataHash and authenticatorData with the private key generated for this account during registration.

4. The authenticator returns the authenticatorData and assertion signature back to the browser.

5. The browser resolves the Promise to a PublicKeyCredential that contains the AuthenticatorAssertionResponse which is returned to the RP to finalize the authentication

Example

Parsed PublicKeyCredential

Note: In the WebAuthn API, the binary values authenticatorData, clientDataJSON and signature are actually of type ArrayBuffer and not Base64 strings. This example displays them as Base64 strings for readability

{
  "id": "X9FrwMfmzj...",
  "response": {
    "authenticatorData": "xGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v-ppaZJdA7cBAAAACA",
    "clientDataJSON": "eyJjaGFsbG...",
    "signature": "MEUCIQDNrG..."
  },
  "clientExtensionResults": {}
}

id: The credential identifier.

response: contains the metadata the RP needs to validate the assertion. The authenticatorData contains the authenticator data. The clientDataJSON contains the JSON-serialized data passed to the authenticator by the client in order to generate the credential. The signature contains the raw signature returned from the authenticator.

clientExtensionResults(): method returns values for zero or more WebAuthn extensions.

Example

Parsed authenticatorData

{
    "authData": {
      "flags": {
        "AT": false,
        "ED": false,
        "UP": true,
        "UV": false
      },
      "rpIdHash": "xGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v-ppaZJdA7c=",
      "signatureCounter": 8
}

authData: The authenticator data is a byte array containing data about the get credential operation, including a signature counter and a hash of the RP ID.

flags: The AT indicates whether the authenticator added attested credential data, and is always 0 in assertions. The ED flag indicates if the authenticator data has extensions. The UP flag indicates if the user is present. The UV flag indicates if the user is verified (PIN or Biometric).

rpIdHash: a SHA-256 hash of the RP ID the credential is scoped to.

signatureCounter: is incremented for each successful authenticatorGetAssertion operation. It is used by RPs to aid in detecting cloned authenticators.

Example

Parsed clientDataJSON

{
  "challenge": "kYhXBWX0HO5GstIS02yPJVhiZ0jZLH7PpC4tzJI-ZcA",
  "origin": "https://demo.yubico.com",
  "type": "webauthn.get"
}

The clientDataJSON object contains the challenge sent by the RP, the origin of the domain observed by the client, and the type of operation performed.

6. Upon receiving the result of the authentication request the server performs the validation of the response by:

  • Verifying the authenticator’s signature using the public key that was stored during the registration request

  • Verifying that the challenge signed by the authenticator matches the challenge that was generated by the server

  • Verifying that the relying party ID is the expected value

The full list of validation steps can be found in the WebAuthn specification.