Ambimat GroupAmbimatAmbiSecureeSIM InitiativeEngineering BlogAhmedabad · India · Est. 1981
DOC WebAuthn reference · v1.0 Updated 2026-05-09 · Static portal · Engineering blog

WebAuthn engineering reference#

This is a developer-portal reference. It assumes you are integrating WebAuthn into a relying-party backend or a JavaScript client; it does not assume you have read the W3C spec end-to-end. Every section links into the AmbiSecure tools that let you walk the binary structures while you read.

If you only have ten minutes, read Registration and Authentication — everything else is depth.

Prerequisites#

  • HTTPS origin — localhost is the only HTTP exception (development).
  • RP ID — pick the broadest registrable domain you want credentials to roam across.
  • CSPRNG — for challenge generation.
  • Persistence — for credentialId, credentialPublicKey, signCount, BE/BS, AAGUID.

Your first ceremony#

The two ceremonies are nearly symmetric. navigator.credentials.create() creates a fresh credential; .get() uses an existing one. Both produce a PublicKeyCredential object with attached binary fields.

JavaScript · client
// Registration — issued by the RP, called by the client
const credential = await navigator.credentials.create({
  publicKey: {
    challenge: challengeFromServer,        // Uint8Array, 32 bytes
    rp: { name: "Example Corp", id: "example.com" },
    user: {
      id: userIdBytes,                  // opaque, max 64 bytes
      name: "alice@example.com",
      displayName: "Alice"
    },
    pubKeyCredParams: [
      { type: "public-key", alg: -7 },     // ES256
      { type: "public-key", alg: -257 }    // RS256
    ],
    authenticatorSelection: {
      authenticatorAttachment: "cross-platform",
      requireResidentKey: true,
      userVerification: "required"
    },
    attestation: "direct"
  }
});

Registration ceremony#

The relying party server has six things to verify on registration. Skip any of these and you've broken the security model.

1. Verify clientDataJSON

Decode the base64url clientDataJSON as UTF-8 JSON. Verify type === "webauthn.create", the challenge matches the one your server issued (single-use), and the origin is in your allow-list (exact match — case-insensitive host, scheme, port).

Use the clientDataJSON decoder to walk the structure during integration.

2. Verify authData.rpIdHash

Compute SHA-256(rpId). Compare against the first 32 bytes of authenticatorData. Mismatch → reject. Common cause: rpId mismatch between client options and server expectation.

3. Verify flags

Verify UP bit (0x01) is set; verify UV bit (0x04) if your policy requires user verification. AT (0x40) MUST be set on registration. Reject if BE=0 ∧ BS=1 (invalid combination per WebAuthn level 3).

4. Verify attestation

If fmt !== "none", validate the attestation signature against authData || SHA-256(clientDataJSON). Validate the x5c chain against your trust anchors and the FIDO MDS BLOB. Walk attestStmt with the attestation decoder.

5. Enforce AAGUID policy

Look up the AAGUID in your cached MDS BLOB. Reject if status is USER_VERIFICATION_BYPASS, ATTESTATION_KEY_COMPROMISE, or any other revocation. Reject if AAGUID is not in your allow-list (enterprise).

6. Persist credential

Store: credentialId (bytes), credentialPublicKey (COSE_Key bytes), signCount (uint32), AAGUID, BE flag, BS flag, transports, and the user binding. The BE flag is immutable for the credential’s lifetime; BS is mutable and may be re-evaluated on every assertion.

Authentication ceremony#

Authentication is the easier half — fewer fields to verify, no attestation, no attestedCredentialData. The signature is the load-bearing primitive.

JavaScript · client
const assertion = await navigator.credentials.get({
  publicKey: {
    challenge: challengeFromServer,        // fresh per-session
    rpId: "example.com",
    allowCredentials: [
      { type: "public-key", id: credentialIdBytes }
    ],
    userVerification: "required"
  }
});

Server verification:

  1. Decode clientDataJSON; verify type, challenge, origin.
  2. Compute SHA-256(rpId); compare with authData.rpIdHash.
  3. Verify UP / UV per policy.
  4. Verify the signature: verify(publicKey, authData || SHA-256(clientDataJSON), signature).
  5. Verify signCount > storedSignCount (or both 0 — many platform authenticators).
  6. Update stored signCount; mint session.

authenticatorData reference#

The exact byte layout of authenticatorData. Use the authData parser to walk live data.

rpIdHash 32 bytes — SHA-256(rpId) flags 1 byte — UP|UV|BE|BS|AT|ED bits signCount 4 bytes BE — counter (anti-clone) if (flags & AT) — registration only aaguid 16 bytes — make/model credLen 2 bytes BE credId credLen bytes — opaque credentialPublicKey COSE_Key (CBOR map) if (flags & ED) extensions CBOR map (e.g. credProtect)

clientDataJSON reference#

JSON object built by the user agent. The bytes are what the authenticator's signature covers (after SHA-256).

clientDataJSON · example
{
  "type": "webauthn.get",
  "challenge": "aabbccddeeff...",    // base64url
  "origin": "https://login.example.com",
  "crossOrigin": false
}

Attestation reference#

The attestationObject is a CBOR map keyed by short strings. See technologies / attestation for the full deep dive on each fmt.

attestationObject = { "fmt": text — "none" / "packed" / "tpm" / "android-key" / ... "attStmt": map — fmt-specific (alg, sig, x5c) "authData": bytes — the binary structure above }

Flags — BE / BS / UP / UV#

BitNameMeaning
0x01UPUser Present (touch / tap).
0x04UVUser Verified (PIN / biometric).
0x08BEBackup Eligible. Set at registration; immutable.
0x10BSBackup State (currently backed up). Mutable.
0x40ATAttested credential data included (registration only).
0x80EDExtension data included.
CHANGELOG v1.0 · Initial publication. WebAuthn level 2 + level 3 BE/BS coverage.

Error handling#

Common DOMExceptions during a ceremony:

  • NotAllowedError — user cancelled or timed out, or browser refused due to RP ID / origin mismatch.
  • InvalidStateError — credential already registered (de-dup mismatch on registration).
  • NotSupportedError — none of the requested algorithms are supported by the available authenticator.
  • SecurityError — RP ID is not a registrable suffix of the origin.
  • ConstraintError — userVerification: "required" but the authenticator can't perform UV.

Found a bug or have a suggestion? File at /contact/. This portal is static and version-pinned; planned migration target is docs.ambimat.com for a future Phase.