← Blog

2026.10.14 · 5 min · security · webauthn · javascript · browsers

How a passkey logs you in.

A passkey is a key pair: the site keeps the public half, your device keeps the private half and signs a fresh challenge every time you log in. That one mechanism explains why a database leak gives attackers nothing and why a phishing site can't use your key. The login flow, the code, and the details people get wrong. As of 2026.10.

"Passkeys replace passwords" is the pitch you hear everywhere. What you rarely hear is why they're safer, and the answer is one mechanism: a key pair and a signature. Once you see it, the two big wins (a leaked database is useless, and phishing doesn't work) stop sounding like marketing and start sounding obvious.

Watch the 60-second version on Instagram.

Passkeys are on the front page again this week, through an essay called Passkeys are a trap. Its thread argues about policy: lock-in, attestation, who controls your keys. Those are fair questions, but you need the mechanism first to judge them, so that's what this post is.

Sign-up: two keys, and the site gets one

When you create a passkey, your authenticator (the phone, the laptop's secure chip, or a password manager) generates a key pair after you consent. The private key stays with the authenticator. The public key goes to the site, which "stores the public key, coupled with the user identity", in MDN's words. That's all the server keeps.

A public key is meant to be public. It can check a signature, but it can't make one. So if the site's database leaks, the attacker has a list of public keys and nothing they can log in with.

Login: sign a fresh challenge

DEVICESERVERCHALLENGE · 9f2c…FACE ID / PINSIGNATUREVERIFIEDGOLD KEY = PRIVATE · OUTLINE = PUBLICFIG. 01 / CHALLENGE, SIGN, VERIFY
  1. Challenge. The server generates a random challenge, at least 16 bytes, new for every login.
  2. Unlock. Your device asks for Face ID, a fingerprint or a PIN. This is user verification, and it only unlocks the key on your device. Your face never goes to the site.
  3. Sign. The authenticator signs the challenge (plus the site's origin) with the private key and sends back the signature.
  4. Verify. The server checks the signature with the stored public key, checks that it's the challenge it just issued, and checks the RP ID (the domain the passkey belongs to).

In the browser, that's one call:

login.js
const cred = await navigator.credentials.get({
  publicKey: {
    challenge,                    // random bytes from the server, fresh each time
    rpId: "example.com",          // must match this site's domain
    userVerification: "required", // Face ID / PIN
  },
});
// send cred.response.signature (and the rest of the response) to the server to verify

The challenge has to be fresh. If a server reuses challenges, a signature captured once can be replayed. Use a WebAuthn library on the server rather than hand-rolling the checks; verifying the signature is the easy part, and checking everything around it is where hand-rolled code goes wrong.

Why phishing doesn't work

A passkey belongs to one domain, its rpId. The highlighted line above isn't a suggestion: the browser rejects an rpId that isn't the page's own domain (or a parent of it), and the authenticator only offers credentials whose rpId matches.

example.comexamp1e.comBROWSERPASSKEYrpId example.comREFUSEDFIG. 02 / THE BROWSER CHECKS THE DOMAIN

So a look-alike site at examp1e.com can copy the real login page pixel for pixel and it still can't ask for your example.com passkey. And because the origin is part of what gets signed, even a signature relayed from a real session doesn't verify for the wrong site. With a password, the human is the one who has to notice the 1. With a passkey, the browser does, before the authenticator ever runs.

What people get wrong

  • "It's a password stored in the cloud." No. Nothing secret is sent to the site, ever. The site gets a public key once and a signature per login.
  • "Face ID data goes to the site." No. The biometric unlocks the key locally; the site only learns that user verification happened.
  • "The key never leaves your device." Not quite. Synced passkeys ("multi-device credentials") move between your devices, end-to-end encrypted. Apple says iCloud Keychain passkeys are encrypted "so even Apple can't read them". And passkeys can now be exported between password managers through FIDO's credential exchange (CXP). The accurate version: the private key never leaves your keychain and never goes to the site. Device-bound passkeys (security keys, for example) really do stay on one device.
  • "Sites can't tell what I'm using." They can ask. Attestation is optional evidence about the authenticator, and sites can use it to accept only certain ones. That's the part of the lock-in debate worth reading about.

WebAuthn Level 3, the spec behind all of this, became a W3C Recommendation on 2026.08.25. Passkeys need a secure context, so HTTPS only.

On my own projects, I'd add passkeys alongside the existing login rather than replacing it, and keep a recovery path that isn't weaker than the passkey itself.

The short version

  1. A passkey is a key pair. The site stores the public key; your authenticator keeps the private key.
  2. Each login, the server sends a fresh random challenge; your device unlocks the key and signs it; the server verifies with the public key.
  3. A leaked public key can't log anyone in.
  4. The browser binds the key to its domain (rpId), so a look-alike site can't use it.
  5. Synced passkeys move between your devices, encrypted; they still never go to the site.

Sources: MDN, Web Authentication API · MDN, PublicKeyCredentialRequestOptions · W3C, Web Authentication Level 3 · passkeys.dev, Terms · Apple Developer, Passkeys · Bitwarden, passkey portability (CXP) · Hacker News, Passkeys are a trap