A passkey is a password your device invents, never shows you, and refuses to hand to anyone, including you. That one property is why passkeys stop phishing, and it is also why a post called "I don't like passkeys" spent a day on top of Hacker News with 616 points and 605 comments. Here is how passkeys work under the hood, and why "un-phishable" and "un-movable" are the same word.
TL;DR
- A passkey is a per-site key pair. The server stores only the public key, so a database leak gives an attacker nothing to log in with.
- At sign-in the device signs a random challenge plus the origin, and the browser writes that origin; the page never touches it. A look-alike domain gets a signature the real server rejects.
- The FIDO Alliance counts 5 billion passkeys in use. Microsoft measured a 98 % sign-in success rate with passkeys against 32 % for passwords: roughly one failed login in fifty instead of two in three.
- The private key cannot be read, so it cannot be copied out either. Export between managers (FIDO's credential exchange format) started shipping in iOS 26 and Android; the protocol next to it is still a working draft. Hardware keys never export, by design.
What is a passkey?
A passkey is a FIDO credential: a public/private key pair that your device (the "authenticator") creates for exactly one website (the "relying party"). Per passkeys.dev, the FIDO/W3C docs site, it is a discoverable credential: the device can find it without the site first sending a username. The spec's older term was "discoverable resident credential", which is why Apple, Google and Microsoft renamed it in 2022.
Two facts carry the whole design:
- One key pair per site. Your passkey for github.com and your passkey for google.com share nothing. A breach reveals only public keys.
- The private key never leaves the authenticator in usable form. You never see it or type it, so there is nothing to reuse and nothing to type into the wrong page.
kenrick95 put the problem on HN in one line: "Passkeys have a marketing problem where no one is able to describe simply what it is."
Where passkeys came from: 2013 to 2022
| When | What happened |
|---|---|
| Feb 2013 | PayPal, Lenovo, Nok Nok Labs and others found the FIDO Alliance to replace passwords |
| 2014 | Google and Yubico ship U2F security keys |
| Mar 2019 | WebAuthn becomes a W3C Recommendation |
| 2022 | Apple, Google and Microsoft commit to FIDO sign-in; Apple ships "passkeys" at WWDC (HN) |
| 2023 | Google accounts get passkeys, then make them the default |
How do passkeys work? The WebAuthn ceremony
The W3C Web Authentication spec calls a registration or a sign-in a "ceremony": a browser call and a signature check.
Registration. The server generates random bytes (the challenge) and the page asks the browser to create a credential. The shape from the spec, as on screen in the video:
const cred = await navigator.credentials.create({
publicKey: {
challenge, // random bytes from the server
rp: { name: "example.com" },
user: { id, name: "niko", displayName: "Niko" },
pubKeyCredParams: [{ type: "public-key", alg: -7 }],
authenticatorSelection: { residentKey: "required" }
}
});
alg: -7 is ES256, ECDSA on the P-256 curve (Trail of Bits has a good walk-through of the cryptography behind passkeys). residentKey: "required" asks for a discoverable credential, which is what makes it a passkey rather than an old-style second factor. The authenticator generates a new key pair for this site and returns the public key and a credential id, which the server stores.
Sign-in. The server sends a fresh challenge, the page calls navigator.credentials.get({ publicKey: { challenge, rpId } }), the user touches the sensor or enters a PIN, and the authenticator signs. The signature covers the authenticator data together with a SHA-256 hash of a small JSON object called clientDataJSON. The server checks the signature against the stored public key. If it verifies, only the holder of the private key could have produced it.
The server holds no password hash and no shared secret: nothing to leak.
Why passkeys stop phishing: the browser writes the origin
The second half is one field. clientDataJSON looks like this (illustrative values):
{
"type": "webauthn.get",
"challenge": "Zm9vYmFy…",
"origin": "https://accounts.google.com"
}
The page does not fill in origin. The browser does, from the address it actually loaded, and checks it against the relying-party id before the authenticator signs anything. JavaScript on the page cannot lie about it. So when you land on accounts.g00gle-login.com, one of two things happens: there is no passkey for that domain at all, or the signature that comes back is bound to the wrong origin and the real server rejects it.
The user can be fooled; the math cannot.
The server's checks, as a simplified sketch (not a complete verifier):
// simplified sketch: what the server must verify on sign-in
clientData.type === "webauthn.get"
clientData.challenge === session.challenge // fresh, single use
clientData.origin === "https://example.com" // exact match
verify(storedPublicKey, signature, authenticatorData + sha256(clientDataJSON))
Skip the origin check and you have put the phishing hole back yourself.
Two caveats that keep "phishing-proof" honest. First, the cross-device path has had bugs: CVE-2024-9956 allowed passkey account takeover in mobile browsers through the hybrid (Bluetooth) transport, since fixed in Chrome, Safari and Firefox. Second, attackers go around the ceremony. A post by @T3chFalcon this month warned that "Hackers are using passkeys as bait to steal Microsoft 365 accounts." The signature can't be phished; the account that still has a password and an SMS fallback can.
Where does the private key live? Synced vs device-bound passkeys
This is where the two stories split.
| Device-bound | Synced | |
|---|---|---|
| Where | a security key such as a YubiKey | iCloud Keychain, Google Password Manager, 1Password, Bitwarden |
| Leaves the hardware? | never | yes, inside an end-to-end encrypted vault |
| Limit | up to 100 per key on YubiKey 5.7 firmware (Yubico), up from 25 | none that matters |
| Tied to | the object in your pocket | the account that owns the vault |
As Trail of Bits notes, sync means the key material does leave the secure element and live in the provider's encrypted vault. That is a deliberate trade, and it moves the risk from "the device" to "the account".
A site can tell which kind it got from the AAGUID, a 16-byte authenticator model id inside the attestation. Most sites ignore it.
Passkey vs password: both stories are true
On the numbers, passkeys win. Google reported passkeys used more than a billion times across 400 million accounts in under a year, "50% faster than passwords". Microsoft, which blocks 7,000 password attacks a second, measured passkey sign-in at three times faster than a password. The FIDO survey of 11,000 adults says 90 % of consumers are familiar with passkeys and 75 % have turned them on for at least some accounts.
On the lifecycle, the complaint is real. Ethan Hawksley calls passkeys a "perfect fit for a corporate environment, but a poor fit for personal security". The risks he lists for a person are "permanent account lockout, automated account bans, and device loss". On hardware keys: "passkeys can only be added or deleted but never moved", so the advice is to buy two or three keys and enrol every site on each one. The HN thread adds friction. drtz: registering passkeys everywhere "becomes a big headache with O(m*n) complexity", m sites times n devices. hannasanarion: "Amazon prompts me to create a passkey every time I log in, even when I logged in with a passkey, because my passkeys live in Bitwarden."
William Brown, author of the webauthn-rs library, wrote in 2024 in "Passkeys: A Shattered Dream": "I'm here saying passwords are a better experience than passkeys. Do you know how much it pains me to write this sentence?" And in July Nikita Bier:
A fair counter-view from the same thread: nunez calls passkeys a "massive quality-of-life improvement", with the catch that almost every site "lays it on top of their traditional user/pass auth flow".
My read: the ecosystem works as designed here. A secret you cannot read is un-phishable and un-movable in the same breath. When the phone dies, or an automated system bans the Google account that holds your vault, every passkey inside goes with it, including the ones for sites that have nothing to do with Google. That is the design seen from the other side.
Can you move passkeys? FIDO credential exchange (CXF and CXP)
The fix has a name. FIDO splits it in two: the Credential Exchange Format (CXF), the file layout for passkeys and passwords, and the Credential Exchange Protocol (CXP), the secure handover between two managers. As of FIDO's specification page, CXF 1.0 is a Proposed Standard and CXP is still a Working Draft, two years after the first drafts.
The format is already shipping. Apple's Passwords app in iOS 26 and macOS 26 imports and exports passkeys with it (Ars Technica). On Android, Credential Manager moves them between Google Password Manager, 1Password, Bitwarden and Dashlane (heise). Hawksley calls it "too immature to rely on": for synced passkeys a fair "not yet".
Device-bound passkeys are the "never". A security key does not export, by design, so the official backup plan is a second security key: a recommendation with a price tag.
What developers should do about passkeys
For your own accounts:
- Keep a fallback until export works for you: a password plus an authenticator app. Your weakest recovery path is your real security level.
- Device-bound means two keys on day one. Enrol both before you need either.
If you build the login:
-
Require a discoverable credential and user verification:
authenticatorSelection: { residentKey: "required", userVerification: "required" }. - Verify the origin and the challenge on the server, every time, with a maintained library.
- Stop asking for a passkey from someone who just used one (see hannasanarion's Amazon prompt).
- Don't make a passkey the only copy of anything. The PRF extension can derive encryption keys from a passkey; Tim Cappalli's post title is the advice: "Don't use passkeys for encrypting user data". If the passkey goes, the data goes with it.
Verdict: NEEDS REVIEW
I stamped passkeys NEEDS REVIEW. The ceremony shipped: one key pair per site, an origin the page can't forge, success rates passwords never reached. The lifecycle is a working draft, literally: that is the label on the protocol for moving passkeys. Until export works everywhere, keep the fallback and buy the second key.
FAQ
Are passkeys safer than passwords?
Against phishing and database leaks, yes. The weak point moves to account recovery and to the account that syncs your passkeys.
What happens to my passkeys if I lose my phone?
Synced passkeys come back when you sign in to that account on a new device. Passkeys on a lost security key are gone; hence the second key.
Can I export passkeys from one password manager to another?
Increasingly. iOS 26 and Android use FIDO's Credential Exchange Format to move them. The exchange protocol is still a working draft, and hardware security keys never export.
Sources
- Ethan Hawksley, "I don't like passkeys": https://hawksley.dev/blog/i-dont-like-passkeys
- Hacker News discussion: https://news.ycombinator.com/item?id=49753211
- W3C Web Authentication Level 3: https://www.w3.org/TR/webauthn-3/
- W3C Web Authentication Level 1 (Recommendation, 2019): https://www.w3.org/TR/webauthn-1/
- passkeys.dev terminology: https://passkeys.dev/docs/reference/terms/
- Trail of Bits, "The cryptography behind passkeys": https://blog.trailofbits.com/2025/05/14/the-cryptography-behind-passkeys/
- Tim Cappalli, "Don't use passkeys for encrypting user data": https://blog.timcappalli.me/p/passkeys-prf-warning/
- CVE-2024-9956 write-up: https://mastersplinter.work/research/passkey/
- FIDO Alliance, Wikipedia: https://en.wikipedia.org/wiki/FIDO_Alliance
- Apple passkeys: https://developer.apple.com/passkeys/
- Google Security Blog, passkeys for Google accounts: https://security.googleblog.com/2023/05/so-long-passwords-thanks-for-all-phish.html
- Google, passkeys by default: https://blog.google/technology/safety-security/passkeys-default-google-accounts/
- Google, one year of passkeys: https://blog.google/technology/safety-security/google-passkeys-update-april-2024/
- Microsoft Security Blog, passkey UX data: https://www.microsoft.com/en-us/security/blog/2024/12/12/convincing-a-billion-users-to-love-passkeys-ux-design-insights-from-microsoft-to-boost-adoption-and-security/
- FIDO Alliance, The State of Passkeys 2026: https://fidoalliance.org/the-state-of-passkeys-2026-global-consumer-and-workforce-report/
- Yubico, YubiKey 5.7: https://www.yubico.com/blog/empowering-enterprise-security-at-scale-with-new-product-innovations-yubikey-5-7-and-yubico-authenticator-7/
- FIDO Credential Exchange specifications: https://fidoalliance.org/specifications-credential-exchange-specifications/download-credential-exchange-specifications/
- 1Password on the CXP/CXF drafts: https://blog.1password.com/fido-alliance-import-export-passkeys-draft-specs/
- Ars Technica, Apple passkey import/export: https://arstechnica.com/security/2025/06/apple-previews-new-import-export-feature-to-make-passkeys-more-interoperable/
- heise, Android password-manager transfer: https://www.heise.de/en/news/Password-managers-on-Android-Switch-without-manual-export-11450892.html
- William Brown, "Passkeys: A Shattered Dream": https://fy.blackhats.net.au/blog/2024-04-26-passkeys-a-shattered-dream/
- Nikita Bier on X: https://x.com/nikitabier/status/2079787406300266743
- @T3chFalcon on X: https://x.com/T3chFalcon/status/2099123163405680837
This article expands on an episode of **The Daily Diff, a five-minute daily video on what shipped and what broke in tech.
Watch the episode · Subscribe on YouTube · the written diff lands in your inbox every morning at thedailydiff.dev.
