Passkeys & WebAuthn
WebAuthn is the web standard for authenticating with public-key cryptography instead of passwords, and passkeys are its consumer-friendly form: credentials stored on your phone or in your password manager, unlocked with Face ID, a fingerprint, or a device PIN, and synced across your devices.
The pitch is not just convenience — it's structural: there is no shared secret to steal. The server stores only a public key, so database breaches leak nothing usable, and the browser cryptographically binds each credential to the real site, making passkeys phishing-resistant in a way passwords and even OTP codes never were. Google, Apple, Microsoft, GitHub, and most major platforms now support sign-in with passkeys.
TL;DR
- A passkey is a key pair: private key stays on the user's device/password manager; the server stores the public key — nothing breach-worthy.
- Login is a challenge–response signature: server sends a random challenge, the authenticator signs it after local biometric/PIN verification.
- Phishing-resistant by construction: the credential is bound to your origin (the Relying Party ID) — it simply won't sign for
examp1e.com. - The biometric never leaves the device — it only unlocks the local key. Servers see a signature, not a fingerprint.
- Passkeys sync through iCloud Keychain, Google Password Manager, 1Password, etc. — solving the "lost device" problem of old hardware-bound WebAuthn.
- Implement with a server library (SimpleWebAuthn, java-webauthn-server, go-webauthn) — never hand-roll the verification.
How It Works
Registration (creating a passkey)
Authentication (logging in)
Two properties do the security work: the challenge is random per attempt (nothing to replay), and the signed payload includes the origin — a signature produced on a phishing site fails verification because the browser, not the user, reports where the ceremony happened.
Quick Example
The browser API, simplified (in practice, a library wraps the encoding):
Server-side, use a maintained library — SimpleWebAuthn (Node/TypeScript), go-webauthn, java-webauthn-server (Yubico), webauthn (python-fido2) — which handles challenge storage, CBOR parsing, signature and origin verification, and sign-count checks.
Core Concepts
Relying Party ID and Origin Binding
The RP ID is your domain (example.com); credentials created for it work on it and its subdomains, and nowhere else. This is the phishing resistance: unlike a password (which users will happily type anywhere), the credential is unusable off-origin by protocol design — no user judgment involved.
Discoverable Credentials and Conditional UI
A discoverable (resident) credential can be found by the browser without the server naming it first — enabling the modern flow where a user taps the username field and the browser offers "Sign in as alice@… with Face ID" (mediation: "conditional"). This, plus sync, is what turns raw WebAuthn into the passkey experience: no username typing, no password, one biometric tap.
Synced vs Device-Bound
Cross-device sign-in is also standard: scan a QR code and approve on your phone (the "hybrid" transport), so a passkey on your phone can log you into a friend's laptop.
What About the Biometric?
Face ID/fingerprint data never leaves the device and is never sent to any server — it only gates the local use of the private key. A server integrating passkeys learns nothing biometric about its users.
Implementing Passkeys in Your App
- Schema: a
credentialstable —user_id,credential_id,public_key,sign_count,transports,created_at,name("Alice's iPhone"). Users can and should register multiple passkeys. - Add alongside passwords first. Offer "Create a passkey" after login (and after password resets — high-motivation moments). Track adoption before considering password removal.
- Login UX: conditional-UI autofill on the username field, plus an explicit "Sign in with a passkey" button.
- Recovery is the real design problem. Passkeys reduce reliance on it, but account recovery (email verification, backup codes, a second passkey) remains the fallback — and attackers know recovery flows are the soft underbelly. Don't let a phishable recovery path undo an unphishable login.
- Watch
signCountanomalies (a decreasing counter can indicate a cloned device-bound credential — informational for synced passkeys, which may not increment).
Common Mistakes
Hand-Rolling Verification
Origin checks, challenge freshness, CBOR/COSE parsing, algorithm allowlists — the attack surface is in the verification details. This is firmly buy-not-build territory; use the established library for your stack.
Reusing or Long-Lived Challenges
The challenge must be random per ceremony, stored server-side (session), expired quickly, and consumed once. A static challenge reintroduces replay attacks.
Treating Passkeys as a Second Factor Only
Passkeys are designed as a primary, single-step factor that's already multi-factor in substance (possession of the device + biometric/PIN). Forcing password-then-passkey wastes their UX advantage; passkey-*instead of*-password is the goal, with MFA logic reserved for step-up on sensitive actions.
Leaving Phishable Fallbacks as the Easy Path
If "forgot passkey? → SMS code" is one click away, attackers phish the fallback. Rate-limit and harden recovery, and require existing-session or stronger verification to remove/add credentials.
Only Allowing One Passkey per Account
Users have multiple devices and ecosystems. Multiple registered credentials (with names and management UI) is table stakes.
FAQ
Passkeys vs passwords + TOTP — really better?
Yes, on both axes. Security: no shared secret to breach or phish (TOTP codes are routinely phished in real time by proxy kits; passkeys can't be). UX: one biometric tap beats typing a password plus a 6-digit code. This is the rare security upgrade users actually prefer.
What if a user loses their phone?
Synced passkeys survive device loss — they're restored with the iCloud/Google/1Password account. The remaining risk shifts to the platform account's security, which is why encouraging a second passkey (or another ecosystem) plus solid recovery still matters.
Can I go fully passwordless?
Incrementally. New consumer apps increasingly launch passkey-first with email-link fallback; existing apps typically run passkeys alongside passwords for years, nudging adoption. Enterprise/IdP contexts (SSO) can enforce passkeys at the IdP and get passwordless everywhere downstream.
How does this relate to FIDO2 and security keys?
FIDO2 = WebAuthn (the web API) + CTAP (the protocol to external authenticators like YubiKeys). "Passkey" is branding for discoverable, usually synced, FIDO2 credentials. Hardware security keys remain the device-bound, highest-assurance end of the same standard.
Do all browsers/platforms support this?
All modern browsers and both mobile platforms support WebAuthn and passkeys; iCloud Keychain, Google Password Manager, Windows Hello, and the major password managers act as authenticators. The compatibility question in 2026 is UX polish per platform, not availability.
Related Topics
- Multi-Factor Authentication — Where passkeys fit in the factor model
- Authentication — Sessions and login architecture
- Password Security — What passkeys replace
- SSO — Enforcing passkeys at the identity provider
- Encryption — The public-key cryptography underneath
- API Security — Protecting what's behind the login