DEV Community

Weather Clock Dash
Weather Clock Dash

Posted on

WebAuthn in Firefox: Developer Guide to Passkeys, Virtual Authenticator, and FIDO2

WebAuthn (Web Authentication API) is the standard that underpins passkeys and security keys. Firefox has comprehensive WebAuthn support. Here's a developer-focused guide.

What WebAuthn Is

WebAuthn is a W3C standard for strong, phishing-resistant authentication. It uses public-key cryptography:

  1. During registration: user's device generates a key pair; public key stored on server
  2. During authentication: server sends a challenge; device signs it with private key; server verifies signature

The private key never leaves the device. The authentication is bound to the origin — signing only works for the exact domain that registered the credential. A phishing site (different domain) cannot intercept or use the credential.

Firefox's WebAuthn Implementation

Firefox implements both Levels 1 and 2 of the WebAuthn spec. Supported authenticator types:

  • Platform authenticators: Windows Hello, macOS Touch ID, iOS Face ID, Android fingerprint
  • Roaming authenticators: FIDO2 security keys (YubiKey, SoloKey, Google Titan)

Key methods:

  • navigator.credentials.create() — register a new credential
  • navigator.credentials.get() — authenticate with existing credential

Basic WebAuthn Flow

Registration

// Check if WebAuthn is available
if (!window.PublicKeyCredential) {
  console.error('WebAuthn not supported');
  return;
}

// Check for platform authenticator (Windows Hello, Touch ID, etc.)
const available = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
console.log('Platform authenticator available:', available);

// Create credential options (from your server)
const options = {
  challenge: new Uint8Array(32), // Random bytes from server
  rp: {
    name: "My App",
    id: "myapp.com"
  },
  user: {
    id: new Uint8Array(16), // User ID bytes
    name: "alice@example.com",
    displayName: "Alice Smith"
  },
  pubKeyCredParams: [
    { type: "public-key", alg: -7 },  // ES256
    { type: "public-key", alg: -257 } // RS256
  ],
  authenticatorSelection: {
    authenticatorAttachment: "platform", // "platform" or "cross-platform" or omit
    userVerification: "required"         // "required", "preferred", or "discouraged"
  },
  timeout: 60000,
  attestation: "none" // "none", "indirect", "direct"
};

try {
  const credential = await navigator.credentials.create({ publicKey: options });
  // Send credential.id, credential.response.clientDataJSON, 
  // credential.response.attestationObject to server
} catch (err) {
  console.error('Registration failed:', err);
}
Enter fullscreen mode Exit fullscreen mode

Authentication

const options = {
  challenge: new Uint8Array(32), // New random bytes from server
  rpId: "myapp.com",
  allowCredentials: [{
    type: "public-key",
    id: new Uint8Array(/* stored credential ID */),
    transports: ["internal", "usb", "nfc", "ble"]
  }],
  userVerification: "required",
  timeout: 60000
};

try {
  const assertion = await navigator.credentials.get({ publicKey: options });
  // Send assertion.response.authenticatorData, assertion.response.signature,
  // assertion.response.clientDataJSON to server for verification
} catch (err) {
  console.error('Authentication failed:', err);
}
Enter fullscreen mode Exit fullscreen mode

Testing WebAuthn in Firefox

Virtual Authenticator in DevTools

Firefox DevTools → Application tab → WebAuthn section.

This section lets you:

  • Add a virtual authenticator (software-simulated security key)
  • Set authenticator type (FIDO2, U2F)
  • Set transport type (USB, NFC, BLE, internal)
  • Toggle user verification support
  • View and manage credentials stored in the virtual authenticator

To test registration without a physical key:

  1. Open Application → WebAuthn
  2. Click "Add Authenticator"
  3. Set type: ctap2, transport: usb, user verification: enabled
  4. Enable "Resident Key" for discoverable credentials (passkeys)
  5. Complete your app's registration flow
  6. The credential appears in the authenticator panel

This is invaluable for automated testing — no physical key required.

Testing Passkeys (Resident Credentials)

Passkeys are "discoverable credentials" — stored on the authenticator, not just a reference:

// For passkey creation, add:
authenticatorSelection: {
  residentKey: "required", // or "preferred"
  userVerification: "required"
}
Enter fullscreen mode Exit fullscreen mode

In the virtual authenticator, enable "Resident Key" to support passkey creation.

Firefox WebAuthn about:config

security.webauth.webauthn → true (enable WebAuthn)
security.webauth.webauthn_enable_softtoken → true (Firefox's internal software token for testing)
security.webauth.webauthn_enable_usbtoken → true (USB security key support)
Enter fullscreen mode Exit fullscreen mode

The webauthn_enable_softtoken is Firefox's built-in test authenticator — different from the DevTools virtual authenticator. For production testing, use physical keys or the DevTools virtual authenticator.

Server-Side Verification

Firefox sends standard WebAuthn data. Server-side libraries:

  • Node.js: @simplewebauthn/server
  • Python: fido2 library
  • Go: github.com/go-webauthn/webauthn

The server must:

  1. Verify the clientDataJSON.type, clientDataJSON.origin, and clientDataJSON.challenge
  2. Parse and validate authenticatorData (RP ID hash, flags, counter)
  3. Verify the signature against the stored public key

WebAuthn vs Passkeys Terminology

  • WebAuthn: The API and protocol standard
  • FIDO2: The broader standard (includes WebAuthn + CTAP2)
  • Passkeys: Marketing term for "discoverable credentials" — passkeys use WebAuthn but are always resident on the authenticator and can sync between devices
  • Security keys: Physical hardware tokens (YubiKey) — use WebAuthn with non-discoverable credentials typically

All of these use the same underlying navigator.credentials API.

Firefox vs Chrome WebAuthn

Both browsers implement WebAuthn spec compliance. Key difference:

  • Chrome: Integrates with Google Password Manager for passkey sync (Android/Chrome ecosystem)
  • Firefox: Platform-level passkey support (OS keychain) without Google dependency

From a developer standpoint: the JavaScript API is identical in both browsers. The difference is in the user experience of passkey sync across devices, not the API.

Debugging WebAuthn Errors

Common errors and causes:

  • NotAllowedError: User cancelled, or timeout. Not an app bug.
  • SecurityError: Origin doesn't match rpId. Check your rpId configuration.
  • InvalidStateError: Credential already exists for this user. Offer to re-register.
  • NotSupportedError: Authenticator doesn't support requested algorithm or attachment.
  • AbortError: Explicitly aborted via AbortController.

In Firefox, these appear in the console with detailed messages. The virtual authenticator in DevTools is the fastest way to reproduce and debug specific error conditions.

Top comments (0)