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:
- During registration: user's device generates a key pair; public key stored on server
- 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);
}
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);
}
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:
- Open Application → WebAuthn
- Click "Add Authenticator"
- Set type:
ctap2, transport:usb, user verification: enabled - Enable "Resident Key" for discoverable credentials (passkeys)
- Complete your app's registration flow
- 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"
}
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)
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:
fido2library -
Go:
github.com/go-webauthn/webauthn
The server must:
- Verify the
clientDataJSON.type,clientDataJSON.origin, andclientDataJSON.challenge - Parse and validate
authenticatorData(RP ID hash, flags, counter) - 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 matchrpId. Check yourrpIdconfiguration. -
InvalidStateError: Credential already exists for this user. Offer to re-register. -
NotSupportedError: Authenticator doesn't support requested algorithm or attachment. -
AbortError: Explicitly aborted viaAbortController.
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)