How Passkeys Work: The Login Flow, Step by Step
A mechanism-level walkthrough of a passkey login: the challenge, the signature, the origin check, and the flag bits a site reads on every sign-in.
Most explanations of passkeys stop at “it uses a key pair instead of a password”. True, and it explains almost nothing about why a passkey cannot be phished, why a site can tell whether your passkey is synced or stuck on one device, or why scanning a login QR code requires Bluetooth to be switched on.
This is the mechanism, in the order it happens. For a plainer overview of what passkeys are and where they are supported, start with passkeys explained; this guide is the layer underneath it.
The two ceremonies
Everything in WebAuthn, the W3C standard passkeys are built on, is one of two operations:
- Registration (
navigator.credentials.create()), which happens once per site per authenticator. A key pair is generated and the public half is handed to the site. - Authentication (
navigator.credentials.get()), which happens on every sign-in. The private half signs a fresh challenge and the site checks the signature.
The specification calls these ceremonies rather than protocols: each involves the user, the browser, the authenticator and the server together, and the security properties hold only if every participant plays its part.
Three parties appear throughout: the relying party (the site, identified by its rpId, normally the registrable domain such as example.com), the authenticator (whatever holds the private key: a phone’s secure element, a laptop TPM, a password manager vault, or a hardware key), and the client (the browser or OS layer between them, which enforces the rules the site cannot enforce for itself).
Registration, step by step
- The server generates a challenge. A random value, large enough that guessing it is not viable, stored server-side against the pending registration.
- The server sends creation options. The challenge, the
rpId, a user handle (an opaque per-account identifier, deliberately not an email address), the acceptable algorithms, and preferences such as whether user verification is required and whether the credential must be discoverable. - The browser validates the
rpIdagainst the page’s own origin. A page atexample.comcannot request a credential forbank.com. This check happens in the browser, not on the server, and it is the first place a phishing site fails. - The authenticator verifies the user. A fingerprint, a face, a device PIN, or a touch. User presence proves somebody physically interacted with the device; user verification proves the device authenticated the specific human.
- The authenticator generates a key pair scoped to that
rpId. The private key never leaves the authenticator’s protected storage. The public key is returned. - The response goes back to the server: a credential ID, the public key, the authenticator data, and a
clientDataJSONobject. - The server verifies and stores. It confirms the challenge matches the one it issued, confirms the origin is its own, then stores the credential ID, the public key, the user handle and the signature counter against the account.
At the end of registration the site holds a public key. There is no shared secret anywhere in the system. A breach of the site’s credential table leaks public keys, which are public by design.
Authentication, step by step
- The server issues a fresh challenge. Fresh matters: reusing a challenge would make a captured assertion replayable.
- The browser calls the authenticator. If the site supplied a list of allowed credential IDs, the authenticator looks for a match. If the site supplied none, the authenticator searches its discoverable credentials for that
rpId, which is what enables sign-in with no username typed at all. - The
rpIdis checked against the page origin again. Same check as registration, same failure mode for a phishing page. - The user is verified. Biometric, PIN, or a physical touch, depending on what the site asked for and what the authenticator supports.
- The authenticator signs. The signature covers the authenticator data bytes concatenated with the SHA-256 hash of
clientDataJSON. This detail is the whole security argument, and the next section unpacks it. - The server verifies the signature against the stored public key, then independently checks the challenge, the origin, the
rpIdhash and the flag bits before it will accept the login.
What actually gets signed
Two structures go into the signed payload, and both carry data the site verifies separately.
clientDataJSON is assembled by the browser, not by the authenticator or the page. It contains:
type, eitherwebauthn.createorwebauthn.get, so a registration response cannot be replayed as an authentication response.challenge, the server’s nonce.origin, the exact origin the ceremony ran on.
Authenticator data is assembled by the authenticator. It contains:
rpIdHash, a SHA-256 hash of the relying party identifier, which lets the server confirm the credential was used for the right site.flags, a single byte of status bits.signCount, a counter intended to increment with each assertion so a server can notice a cloned authenticator.
The signature is computed over authenticator data followed by the hash of clientDataJSON. That means the origin is inside the signed message. A phishing site at examp1e.com cannot obtain a signature over origin: "https://example.com", because the browser writes the real origin into clientDataJSON and the authenticator signs whatever the browser wrote.
This is the difference that matters against a one-time code. A TOTP code is six digits with no idea where it is being typed, so a relay attack works: the phishing page collects the code and replays it inside the validity window. A passkey assertion is bound to an origin cryptographically, and a proxy in the middle cannot rewrite a signed field without invalidating the signature. Phishing resistance is a property of the message format, not of the user’s judgement. NIST’s SP 800-63B treats this as a category difference too: a hardware-bound cryptographic authenticator is what reaches the highest authenticator assurance level, and one-time codes cannot.
The flag bits a site reads on every login
The flags byte is small and surprisingly informative:
| Flag | Meaning | What a site does with it |
|---|---|---|
| UP | User presence: somebody physically interacted with the authenticator | Baseline requirement on almost every ceremony |
| UV | User verification: the authenticator authenticated the user with a biometric or PIN | Lets the site treat the passkey as two factors in one gesture |
| BE | Backup eligibility: this credential is allowed to be backed up | Tells the site the credential is multi-device capable |
| BS | Backup state: the credential is currently backed up | Tells the site whether the key exists in more than one place right now |
BE and BS let a site distinguish a synced passkey from a device-bound one. A credential with BE unset lives on exactly one authenticator and always will, the normal state for a passkey created on a hardware security key. That is why some services prompt you to register a second credential after you add a passkey: they read BE, see a single-device credential, and correctly conclude that losing the device locks you out.
Where the private key actually lives
The FIDO Alliance splits passkeys into two families: synced passkeys, replicated between a user’s devices through a cloud service, and device-bound passkeys, which never leave the single authenticator that created them.
In practice there are three storage locations, and the choice determines your recovery story more than your security posture:
- Platform authenticator. The secure element or TPM in a phone or laptop, synced through iCloud Keychain or Google Password Manager. Convenient, and tied to that ecosystem’s account.
- Password manager vault. 1Password, Bitwarden and Dashlane can hold passkeys, making them portable across platforms rather than across one vendor’s devices. The vault then becomes the single thing protecting every credential, which is the argument for putting a hardware key on the vault itself. The Bitwarden versus 1Password comparison covers how the two differ once they hold passkeys, and Bitwarden’s export documentation notes that stored passkeys travel in the JSON export but not in a CSV.
- Hardware security key. A YubiKey stores discoverable credentials directly. Device-bound, no sync, no recovery beyond the spare key you enrolled at the same time. The guide to choosing and enrolling a YubiKey covers why buying two is not optional.
Signing in on a device that has no passkey
Cross-device authentication is the flow behind the QR code. The FIDO Alliance describes it as the CTAP 2.2 hybrid transport: the device you are signing in on displays a QR code, your phone scans it, and Bluetooth Low Energy is used to verify physical proximity between the two devices before the assertion is produced.
The BLE step is not for data transfer and it is not incidental: it is what stops the QR code being useful to a remote attacker. A screenshot of a login QR code pasted into a chat is worthless, because the phone that scans it must be physically near the screen that displayed it. Same reasoning as the origin check: the protocol removes a decision from the user rather than asking them to make it correctly.
A worked example
A concrete sign-in at example.com, with a synced passkey in a password manager:
- You click “Sign in”. The page calls
navigator.credentials.get()with a challenge of9f2c…and no credential list. - The browser confirms the page origin is
https://example.comand passesrpId: "example.com"down. - Your password manager finds one discoverable credential for that
rpIdand prompts for Face ID. - The authenticator builds authenticator data with
rpIdHash = SHA-256("example.com"), sets UP and UV, sets BE and BS because the credential is synced, and increments the counter. - It signs
authenticatorData || SHA-256(clientDataJSON), whereclientDataJSONreads{"type":"webauthn.get","challenge":"9f2c…","origin":"https://example.com"}. - The server verifies the signature with the stored public key, then checks the challenge, the origin string and the
rpIdHash. All pass, and the session cookie is issued.
Run the same flow against a phishing page at examp1e.com and step 2 fails: the browser will not pass rpId: "example.com" from a page whose origin is examp1e.com. If the attacker instead requests a credential for their own domain, the authenticator has none, and any signature they do obtain carries origin: "https://examp1e.com" inside the signed bytes, which the real server rejects.
What this design does not fix
The cryptography is sound. The system around it is where the residual risk sits:
- Account recovery. A site offering passkey login plus an email magic-link reset has an authentication strength equal to your email account, not to the passkey. This is the most common way passkey deployments end up weaker in practice than on paper.
- Device compromise. Malware controlling an unlocked device can request assertions from the local authenticator. Origin binding does not help against code running inside the origin.
- The sync fabric. A synced passkey is protected by whatever protects the sync account, which is exactly why that Apple, Google or password manager account deserves a hardware key.
- Uneven support. Coverage is broad but not universal, so strong unique passwords remain the baseline everywhere else. The password security fundamentals guide covers that baseline, and the case for a dedicated vault over browser-saved passwords does not change when passkeys enter the picture.
Bottom line
A passkey login is a signature over a challenge plus an origin, produced by a key that never leaves your authenticator and verified against a public key the site could publish without harm. The phishing resistance comes from the origin sitting inside the signed bytes, written by the browser rather than the page. The residual risk comes from everything the specification does not cover.
Choosing where to keep passkeys is really a choice about which vault you trust. The password manager matcher ranks the options against your constraints, and the best free password manager guide covers the no-cost options that store passkeys.
See also
- Passkeys explained: how they work and when to use them
- YubiKey for password manager 2FA
- Bitwarden vs 1Password 2026
- Password security fundamentals
Related across the network
- How to Prevent Browser Fingerprinting: What Works — anonguide.com
- Keycloak vs authentik vs Authelia: What to Self-Host — keycloakops.com
Sources
Related
Passkeys explained: how they work and when to use them
An explanation of passkeys (FIDO2/WebAuthn): what they are, why they resist phishing, where they are supported, and how they work with password managers.
YubiKey for Password Manager 2FA: Which Key and Setup
Hardware keys protect a password vault from phishing and credential stuffing. Which YubiKey to buy and how to enroll it on Bitwarden and 1Password.
Password security fundamentals: what actually matters
The credential security basics that actually matter in 2026: password length, uniqueness, breach exposure, phishing-resistant 2FA, and passkeys.