Passkeys / WebAuthn
Native browser WebAuthn APIs for phishing-resistant, passwordless authentication. No client-side wrapper libraries — server-side verification libraries are listed in the overview.
Polyfill requirement: Install webauthn-polyfills in every project. The JSON helpers and getClientCapabilities() are Baseline Newly Available (2025); the polyfill backfills during the browser adoption window.
I need to...
| I need to... | Guide | Summary |
|---|---|---|
| Verify HTTPS, RP ID, and server library before writing passkey code | Passkeys Prerequisites & Overview | Verify HTTPS context, consistent RP ID, and residentKey:required before writing passkey code. Use getClientCapabilities() to gate UI. Server-side: pick a vetted library (SimpleWebAuthn for JS/TS); never hand-roll WebAuthn crypto. |
| Register a new passkey from a button or settings panel | Passkey Registration | Use residentKey:required for discoverable credentials. Segregate try/catch into two blocks — the inner wraps credentials.create() (abort on catch, never signal), the outer wraps the server fetch (signal signalUnknownCredential on rejection). Missing this split causes signalUnknownCredential to fire on user-cancel events. |
| Silently register a passkey right after a successful password login | Conditional Create | Trigger immediately after a complete password-based sign-in (not magic links or OTP). Abort any active autofill controller first. Pass mediation:'conditional' to suppress the blocking modal. Never render error UI for InvalidStateError or NotAllowedError in this flow. |
| Implement passkey sign-in (autofill suggestions or button) | Passkey Authentication | Run initConditionalAutofill() on DOMContentLoaded with mediation:'conditional'. Button flow must abort the autofill controller first, then re-arm autofill after exiting. Signal signalUnknownCredential only on HTTP 404 — not on any other server error. Store userVerification level server-side to prevent client manipulation. |
| Show users their saved passkeys and allow rename or delete | Passkey Management | Call signalAllAcceptedCredentials on page load and after every delete to keep password manager vaults in sync. All Signal API calls require Base64URL strings — never ArrayBuffer rawId. Gate every Signal API call with feature detection; Firefox does not support it. AAGUID is UX-only — never use it for access control. |
| Re-verify a signed-in user before a sensitive action | Passkey Reauthentication | Populate allowCredentials with the current user's credential IDs — never leave it empty for reauthentication or any user's passkey can satisfy the challenge on a shared device. After signature verification, explicitly assert the credential belongs to req.user.id. Button-only trigger — never autofill. |