WebAuthn and Passkeys on Windows: From CTAP to the Credential Provider Model
The know/have/are taxonomy collapses against modern phishing kits. Passkeys, WebAuthn Level 3, CTAP 2.x, and Windows 11 24H2 third-party providers score against the criteria that actually matter -- and recovery is the load-bearing column.
Permalink1. Two factors, no security
A junior engineer at a mid-size firm types her Microsoft 365 credentials into what looks exactly like the real login.microsoftonline page, approves the push notification on her phone, and an hour later the security team is reading her inbox -- because the attacker was, too. The kit is Tycoon 2FA, the technique is reverse-proxy adversary-in-the-middle, and the marketing claim that "password plus MFA is two factors" just lost to a commodity off-the-shelf service. The same class of phishing-as-a-service kit (Evilginx, Caffeine, EvilProxy, Tycoon 2FA) is the dominant phishing toolset in 2024-2026; the kit sits between the user and the real Microsoft login page, captures the credentials and the post-MFA session cookie in flight, and hands a live session to the attacker [1].
Replay the exact same attack against a colleague whose only authenticator is a WebAuthn passkey. The kit serves the look-alike page; the page hands the browser a WebAuthn PublicKeyCredentialRequestOptions blob with a fresh challenge. The browser builds clientDataJSON with type: "webauthn.get", the actual origin the user is on (the look-alike domain login-microsoft0nline.example, protocol scheme included), and the challenge. The authenticator computes the RP-ID hash from that origin, looks up its stored credential, and finds nothing -- it never registered a passkey for that domain. There is no signature to relay. The kit gets bytes that the real Microsoft server will reject on the first verification step. Microsoft's own documentation puts it bluntly: passkeys "use origin-bound public key cryptography, ensuring credentials can't be replayed or shared with malicious actors" [2].
The know/have/are taxonomy ranks these two ceremonies as the same. Password plus push is "something you know" plus "something you have," and so is password plus a passkey on a YubiKey. The taxonomy predicts that both ceremonies are roughly twice as strong as one factor alone. The phishing kit demolishes one and bounces off the other. The taxonomy is wrong.
The right question is not "how many factors did the user produce?" It is "what does the attacker have to defeat?" The know/have/are buckets group authenticators by what the user feels they are producing. The criteria framework groups them by what an attacker has to defeat. Only the second taxonomy predicts the outcome of a real-world attack. The phishing kit walks through password plus push because nothing in that ceremony binds the user's secret to a specific origin. It bounces off the passkey because the passkey signs over the origin the browser is actually on, and no amount of reverse proxying changes that string.
If the taxonomy is wrong, what is the right one? That is the question §2 answers.
2. The criteria framework: five axes that actually predict outcomes
The replacement for know/have/are is a five-row table. The rows are what an attacker has to defeat, not what the user thinks they are producing. The spine of the table is taken from NIST SP 800-63-4 (final, July 2025) [3], NIST SP 800-63B-4 [4], the FIDO Alliance Authenticator Certification Levels [5], and the IETF channel-binding lineage that runs from RFC 5056 (Williams, November 2007) [6] through RFC 9266 (Whited, July 2022) [7].
An authenticator whose protocol prevents a relying party impersonator (an adversary-in-the-middle) from inducing the authenticator to release a usable credential value. NIST SP 800-63B-4 formalises the requirement as verifier-impersonation resistance. The practitioner formulation, courtesy of Yubico, is verbatim: an authenticator is phishing-resistant if it binds its output to a communication channel or a verifier name [8].
Axis 1: phishing resistance
The criterion: can a look-alike domain induce the user (or the user's authenticator) to release a credential value that the look-alike then replays to the real verifier? Password plus any unbound second factor (SMS-OTP, TOTP, push) fails the criterion -- the kit just forwards every value the user produces. WebAuthn passes it by construction: the authenticator signs over clientDataJSON, which the browser fills in with the actual origin the user is on, and the signature is computed jointly over a hash of the RP identifier derived from that origin. The RP refuses any signature whose RP-ID hash does not match the registered rpId.
The mechanism by which WebAuthn enforces phishing resistance: the browser writes the user's actual origin into clientDataJSON.origin, the authenticator signs over the SHA-256 hash of the canonical RP identifier (rpIdHash in authenticatorData), and the relying party validates that rpIdHash matches the RP identifier under which the credential was registered. The cryptography is trivial. The value is in the binding.
Microsoft's Entra documentation states the criterion verbatim: passkeys "provide verifier impersonation resistance, which ensures an authenticator only releases secrets to the Relying Party (RP) the passkey was registered with and not an attacker pretending to be that RP" [2].
Axis 2: verifier-compromise resistance
The criterion: if the relying party's authentication database is exfiltrated, can the attacker use the stolen material to log in? Passwords fail this criterion in the worst possible way -- a salted hash is replayable after offline cracking, and a billion-row password dump is the standard primary input to credential stuffing. The public-key model passes the criterion definitionally. The relying party stores only the credential's public key; no signature is ever made by the relying party. Even a complete database leak gives the attacker zero authenticators.
This criterion is older than WebAuthn by half a century. Morris and Thompson's 1979 password paper made the verifier-compromise case for hashing passwords on a multi-user UNIX system [9]; the WebAuthn move is the realisation that even bcrypt'd password databases lose this criterion eventually, because the work factor that protects them today is one Moore's-law decade away from being trivial.
Axis 3: replay and relay resistance
The criterion: can an attacker who observes one successful authentication replay it later, or relay it to a different verifier? OTP-based ceremonies (HOTP [10], TOTP [11]) provide partial replay resistance via a per-instance counter or timestamp, but they offer almost no relay resistance: the AitM kit forwards the OTP through its proxy within the OTP's validity window.
WebAuthn passes the criterion with three layered mechanisms. The first is a fresh challenge issued by the RP for every ceremony, which the authenticator signs over. The second is a per-credential signature counter included in authenticatorData, monotonically increasing on each use (the relying party rejects any assertion whose counter is not strictly greater than the previous one, modulo the synced-passkey carve-out we will reach in §7). The third is channel binding -- the structurally correct answer to relay attacks, which sits at the TLS layer rather than the application layer.
clientDataJSON.tokenBinding field is therefore a no-op in 2026 production. WebAuthn solves the criterion above the channel by signing the origin into the assertion itself.
The cleaner channel-binding answer is RFC 9266 tls-exporter for TLS 1.3 (Whited, July 2022) [7], which extends RFC 5056's channel-binding framework into the TLS 1.3 world -- but no major browser wires tls-exporter into WebAuthn out of the box as of January 2026. The current WebAuthn deployment treats the origin string in clientDataJSON as the primary channel binding, with HTTPS itself providing the underlying TLS guarantee.
Axis 4: step-up and session continuity
The criterion: can the relying party demand a fresh authentication for a high-value action (transfer money, change password, invite a user), and can it tell the difference between a session that was authenticated with strong factors and one that was authenticated with weak factors? WebAuthn answers this with two flag bits in authenticatorData. UP (user present) is set when the authenticator detected a presence test -- a touch, a click, an NFC tap. UV (user verified) is set when the authenticator additionally verified the user via PIN, biometric, or other gesture. A relying party that demands userVerification: "required" can force UV=1 on the assertion; an RP that issues a fresh challenge for a high-value action gets a fresh signature tied to that challenge.
Generic transactional confirmation -- "sign a description of this specific transaction" -- was attempted in WebAuthn's earliest drafts via the txAuthSimple and txAuthGeneric extensions [17]. Neither extension was ever implemented by browsers, and both are absent from the Level 3 specification surface as of January 2026 [18]. The Secure Payment Confirmation flow in WebAuthn Level 3 [19] is the productised replacement for payment transactions; general transactional authorisation remains an open problem.
Axis 5: recovery and lifecycle
The heretical thesis: this is the only axis that matters in production, and it is the axis on which every modern platform still bottoms out at a single-factor primitive. We will foreshadow it here and land on it in §17. A passkey ceremony that scores AAL3 phishing-resistant at the authentication moment can be a single-factor SMS-OTP at the recovery moment -- and the system's AAL is the recovery flow's AAL, not the authentication ceremony's. Microsoft's Entra documentation already flags account recovery as a load-bearing deployment cost: FIDO2 keys "can increase costs for equipment, training, and helpdesk support -- especially when users lose their physical keys and need account recovery" [2].
The criteria table as a spine
The five axes give the article its spine. Every later section fills in a row of the same five-column table. The columns are the strongest authenticators we have shipped: password, password plus SMS-OTP, password plus TOTP, password plus push with number matching, device-bound FIDO2 hardware key, synced passkey, and a hypothetical "recovery-flow-aware" composite. The criteria-aware ranking (§13) re-orders that table in a way the know/have/are taxonomy cannot.
The know/have/are taxonomy groups authenticators by what the user feels they are producing. The criteria framework groups them by what an attacker has to defeat. Only the second taxonomy predicts the outcome of a real-world attack.
If these are the right axes, when did we figure that out?
3. Where the taxonomy came from
The know/have/are taxonomy did not appear all at once. The 1970s and 1980s operating-systems literature already grouped authentication factors into "something the user knows," "something the user has," and "something the user is" -- it was a way of talking about the design space, not a regulatory criterion. The taxonomy entered U.S. federal procurement via the Department of Defense's Trusted Computer System Evaluation Criteria in December 1985 -- the Orange Book, DOD 5200.28-STD [20] -- which required identification and authentication at every assurance class above D and made passwords the canonical something you know in federal IT. The Orange Book did not invent the taxonomy; it codified it.
Two decades later, in June 2004, NIST canonised the same taxonomy as the U.S. federal regulatory framework. NIST SP 800-63 Electronic Authentication Guideline -- by William Burr, Donna Dodson, and W. Timothy Polk -- defined four assurance levels and tied each to a combination of authenticator categories that the levels could accept [21] [22]. Burr's framework absorbed two decades of accumulated practice with hardware OTP tokens. The canonical commercial OTP product, RSA SecurID, had shipped in 1986 -- a key fob that produced a fresh code each minute using a built-in clock and a factory-encoded seed [23] -- and SP 800-63 explicitly accepted SecurID-class authenticators at the higher assurance levels. The four-level structure (later AAL1 through AAL3 in the post-2017 redesign) lasted through SP 800-63-1 (2011), -2 (2013), -3 (2017), and -4 (2025); every revision is recognisably the same shape [24].
The CSRC bibliographic page for the 2004 first edition renders the leading author as a blank entry preceded by a stray comma, an artefact of Burr's retirement from NIST after publication. The actual cover-page authorship is Burr, Dodson, and Polk -- the citation in the references list above uses the correct three-name form.In parallel, the cryptographic protocol literature was building the criteria taxonomy that would eventually displace know/have/are. Bellcore's Neil Haller published RFC 1760 in February 1995 -- the S/KEY one-time password system, a Lamport hash chain that produced a fresh login secret each time and that an eavesdropper could not replay [25]. Haller's text already says the technique was first suggested by Leslie Lamport, which makes 1995 the first IETF standardisation of replay-resistance as a design criterion. RFC 4226 (HOTP, December 2005) [10] and RFC 6238 (TOTP, May 2011) [11] generalised the same idea into the synchronised counter and time-based variants the world now calls "authenticator app" codes.
The verifier-impersonation criterion got its first IETF expression in November 2007. Nico Williams' RFC 5056 On the Use of Channel Bindings to Secure Channels defined the concept that "the two end-points of a secure channel at one network layer are the same as at a higher layer," and bound authentication at the higher layer to the channel at the lower layer [6]. RFC 5056 was the protocol-literature acknowledgement that authentication needed to be tied to something the network attacker could not change -- the channel itself, not just the user's typing.
Kim Cameron's The Laws of Identity, published on identityblog.com in May 2005, captured the same idea from a higher-level perspective. The seven Laws are a framework for federated identity on the open Internet; Laws 2 ("minimal disclosure for a constrained use") and 4 ("directed identity") are the conceptual ancestors of WebAuthn's origin binding and per-RP key pair design [26]. Cameron was Microsoft's Chief Identity Architect through this period, and the Laws shaped a generation of Microsoft thinking on identity. The Laws preceded the consortium that would actually ship the protocol by eight years.
Diagram source
gantt
title Authentication standards lineage, 1985-2026
dateFormat YYYY
axisFormat %Y
section Regulatory codification
Orange Book DOD 5200.28-STD :1985, 5y
NIST SP 800-63 v1 :2004, 7y
NIST SP 800-63-3 (phishing-resistant) :2017, 8y
NIST SP 800-63-4 final :2025, 2y
section Criteria origin (IETF/W3C)
RFC 1760 S/KEY :1995, 10y
RFC 4226 HOTP :2005, 6y
RFC 5056 Channel binding :2007, 4y
RFC 6238 TOTP :2011, 7y
RFC 8471 Token Binding :2018, 1y
RFC 9266 tls-exporter :2022, 4y
section Identity literature
Cameron Laws of Identity :2005, 8y
section FIDO and W3C
FIDO Alliance launch :2013, 1y
FIDO U2F 1.0 :2014, 5y
WebAuthn FPWD :2016, 3y
WebAuthn L1 + CTAP 2.0 :2019, 2y
WebAuthn L2 + CTAP 2.1 :2021, 1y
Passkey commitment May 2022 :2022, 1y
WebAuthn L3 CR :2023, 3y
CTAP 2.2 PS :2025, 1y
section Windows
Windows 10 1903 webauthn.dll :2019, 3y
Windows 11 22H2 ECC :2022, 2y
Windows 11 24H2 plug-in model :2024, 2y By 2007 the criteria framework was on paper. By 2013 there was a consortium for it: the FIDO Alliance launched on 12 February 2013 [27], with six founding members [28]. Earlier identity-layer attempts -- Mozilla Persona / BrowserID, launched July 2011, with decommissioning announced January 2016 and the service shut down on 30 November 2016 [29] -- had tried to build a browser-mediated identity layer at the HTTP level and failed to achieve traction. The FIDO consortium took a different bet: solve the authentication ceremony first, leave the identity-layer above it to OIDC and SAML. What happened first in a browser?
4. U2F: the first browser ceremony designed against phishing
December 2014. Yubico, Google, and NXP Semiconductors publish FIDO 1.0 / Universal 2nd Factor (U2F) [30]; U2F 1.0 reached Proposed Standard status on 9 October 2014, with the broader FIDO 1.0 announcement window running through December [31]. The Universal 2nd Factor Wikipedia article catalogues the design tradeoffs explicitly: U2F's challenge-response is "signed (encoding originating domain/website) to prevent interception and reuse" [31]. This was the first time a browser ceremony was designed against the phishing-resistance criterion as a primary goal rather than as an afterthought.
The U2F ceremony has five field-level moving parts. An AppID string identifies the relying party, derived from the page's origin so a phisher's domain cannot produce a U2F signature for the real bank. A challenge is a per-ceremony nonce the relying party generates. A key handle is an opaque blob the authenticator returns at registration and supplies on every later assertion; the relying party uses it to address the right credential on the next challenge. A signature counter increments monotonically on every assertion, letting the relying party detect simple cloning. And the signature itself is an ECDSA P-256 signature over the AppID hash, the challenge, the counter, and a presence flag.
The AppID rule is the load-bearing piece. The browser computes the AppID from the actual origin the user is on; the authenticator signs over its hash; the relying party compares it to the AppID under which the credential was registered. A look-alike domain produces a different AppID, which produces a different signature, which the real verifier rejects. This is the same trick WebAuthn will later generalise as rpId binding -- and it is the trick that makes U2F structurally immune to the AitM kits that will demolish password plus push a decade later.
The canonical deployment paper is Security Keys: Practical Cryptographic Second Factors for the Modern Web, by Juan Lang, Alexei Czeskis, Dirk Balfanz, Marius Schilder, and Sampath Srinivas, in the Financial Cryptography 2016 preproceedings [32]. The paper documents Google's internal rollout: a hardware second factor for every employee, replacing the company's previous OTP-based MFA. The empirical scoreboard for the criteria framework gets its first data point here -- after the rollout, Google reported zero phishing-related account takeovers on employee accounts during the deployment period. This is not a controlled study; it is the largest natural experiment in deployed phishing resistance the industry had seen.
U2F's limitation is that it is, by design, a second factor. The password under it remains the load-bearing weak link: a credential-stuffer can reuse the password against a service that does not require U2F, and a phisher can still capture the password even if they cannot capture the U2F signature. The AppID idea was correct; what was missing was the willingness to make the strong factor the factor, not a layer on top of a weak one. The bridge from U2F to FIDO2 is exactly that move.
The other piece U2F got right and FIDO2 inherited is the principle that the credential is device-bound by default. The U2F Wikipedia summary captures the consequence: "no recovery of the key is possible" if the device is lost [31]. This is the same property that makes synced passkeys, when they arrive in May 2022, a productisation rather than a cryptographic move. The bytes are the same. The lifecycle is different.
If the second factor is doing all the work, why not make it the factor?
5. FIDO2 + CTAP 2.0 + WebAuthn Level 1: the spec lands
March 4, 2019. The World Wide Web Consortium and the FIDO Alliance announced that the Web Authentication specification was an official W3C Recommendation [33]; the dated Recommendation slug is REC-webauthn-1-20190304 [34]. Same day, with January 30, 2019 as the underlying CTAP 2.0 Proposed Standard date [35]. The pair is what the industry markets as FIDO2.
The reframe was decisive. A platform authenticator -- Windows Hello on Windows, Touch ID on macOS, the Android Keystore on Android -- was now a first-class FIDO authenticator. The user's laptop or phone could be the authenticator. The browser did not need a separate USB device; it could call into the OS instead. This is the move that made FIDO2 a consumer technology, not just a security-team technology.
The relying party is the web service that owns the user's account. The rpId is a string identifying that party for credential scoping; it must be a registrable suffix of the page's origin (so login.bank.com may use bank.com as its rpId, but evil.com may not). All WebAuthn signatures are made over the SHA-256 hash of the rpId, which the browser derives from the actual origin and writes into clientDataJSON. The relying party validates the signature against the public key registered for that rpId. Phishing resistance is rpId binding, full stop [19].
The Web IDL surface that WebAuthn Level 1 standardised is small. navigator.credentials.create({publicKey: ...}) registers a new credential; navigator.credentials.get({publicKey: ...}) produces an assertion. Both return PublicKeyCredential objects. The complexity is not in the API; it is in the byte-level structures the API exchanges.
A registration ceremony looks like this. The relying party generates a PublicKeyCredentialCreationOptions blob containing a fresh challenge, the rpId, the user's account identifier, the list of algorithms the RP supports, the desired user verification, and an optional list of credentials the user already has. The browser passes this to the authenticator and gets back two byte blobs. The first is clientDataJSON -- a UTF-8 JSON blob containing type: "webauthn.create", the origin the browser was actually on, and the challenge. The second is authenticatorData -- a binary blob containing the rpIdHash (SHA-256 of the canonical rpId), the flags byte (with UP, UV, AT, ED bits), the signature counter (initially zero, sometimes non-zero), the new credential's identifier, the AAGUID identifying the authenticator model, and the credential's public key in COSE_Key format. An optional attestation statement binds those bytes to a hardware root of trust.
A 16-byte identifier the authenticator includes in authenticatorData to identify its make and model. Some authenticators emit an all-zeros AAGUID for privacy. Microsoft's Entra ID hardware-vendor matrix lists dozens of FIDO2 keys with their AAGUIDs and supported transports [36]; the FIDO Metadata Service is the authoritative directory.
Diagram source
sequenceDiagram
participant U as User
participant B as Browser
participant A as Authenticator
participant R as Relying Party
R->>B: PublicKeyCredentialCreationOptions {challenge, rpId, user, pubKeyAlgs}
B->>B: build clientDataJSON {type:create, origin, challenge}
B->>A: authenticatorMakeCredential(clientDataHash, rpId, user, ...)
A->>U: prompt for user gesture (UV)
U->>A: present gesture (PIN, fingerprint, face)
A->>A: generate (pubKey, privKey) and sign attestation
A->>B: clientDataJSON, authenticatorData, attestationStatement
B->>R: attestationResponse {clientDataJSON, attestationObject}
R->>R: verify origin, rpIdHash, signature, then store pubKey, credentialId
R->>U: account created An authentication ceremony is the same shape with one structural change: the RP supplies PublicKeyCredentialRequestOptions with a fresh challenge, the authenticator finds the credential matching the rpId, prompts the user for a gesture (if userVerification is requested), and produces an assertion -- a signature over authenticatorData || SHA-256(clientDataJSON) with the credential's private key. The relying party verifies the signature against the stored public key.
The Windows-side surface debuts in the same window. Microsoft Learn states verbatim that Microsoft "introduced the W3C/Fast IDentity Online 2 (FIDO2) Win32 WebAuthn platform APIs in Windows 10 (version 1903)" [37]. May 2019. webauthn.dll ships. From that moment on, every browser on Windows -- Edge, Chrome, Firefox, Brave -- talks WebAuthn through one Win32 surface. The Microsoft Learn passkey overview makes the underlying architecture explicit: "When these APIs are in use, Windows 10 browsers or applications don't have direct access to the FIDO2 transports for FIDO-related messaging" [37]. The OS is the dispatcher.
The W3C/FIDO press release named the launch implementations: Windows 10, Android, Chrome, Firefox, Edge, and Safari (in preview) [33]. Microsoft, Google, Mozilla, and Apple all shipped within the same year. WebAuthn became the most-implemented strong-authentication standard on the consumer web inside eighteen months.
// A reader can paste in their own clientDataJSON and authenticatorData
// (base64url-encoded as Microsoft returns them) to see how the parser
// walks the bytes. Origin binding is one SHA-256 invocation away from
// being a one-liner; the value is in the binding, not the cryptography.
const clientDataB64 = "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0Iiwib3JpZ2luIjoiaHR0cHM6Ly9sb2dpbi5taWNyb3NvZnRvbmxpbmUuY29tIiwiY2hhbGxlbmdlIjoiUk5KU2V6NjFqdyJ9";
const authDataB64 = "Y9JZsAcVeQOLgxs9Ux7QYZpyTaB-OkpdyPwQk7P9YsoFAAAAFw";
function b64urlDecode(s) {
s = s.replace(/-/g,'+').replace(/_/g,'/');
while (s.length % 4) s += '=';
return Uint8Array.from(atob(s), c => c.charCodeAt(0));
}
const clientDataBytes = b64urlDecode(clientDataB64);
const clientData = JSON.parse(new TextDecoder().decode(clientDataBytes));
console.log("clientDataJSON.type =", clientData.type);
console.log("clientDataJSON.origin =", clientData.origin);
console.log("clientDataJSON.challenge=", clientData.challenge);
const authData = b64urlDecode(authDataB64);
const rpIdHash = authData.slice(0, 32);
const flags = authData[32];
const signCount = (authData[33]<<24) | (authData[34]<<16) | (authData[35]<<8) | authData[36];
console.log("authenticatorData rpIdHash =", Array.from(rpIdHash).map(b=>b.toString(16).padStart(2,'0')).join(''));
console.log("authenticatorData flags = 0x" + flags.toString(16),
"UP="+(flags&1), "UV="+((flags>>2)&1), "BE="+((flags>>3)&1), "BS="+((flags>>4)&1), "AT="+((flags>>6)&1));
console.log("authenticatorData signCount=", signCount); Press Run to execute.
Level 1 settled the field-level shape. What did the next two years sharpen?
6. CTAP 2.1: the wire protocol every security key is speaking
15 June 2021. The FIDO Alliance published CTAP 2.1 as a Proposed Standard [39]. CTAP 2.1 is the CBOR-on-the-wire version most security keys in 2024-2026 are running; CTAP 2.2 (Proposed Standard, 14 July 2025) [40] refines a few corners, and CTAP 2.3 is the Proposed Standard the FIDO Alliance lists alongside it [41]. Each version adds capability without breaking the previous one's commands.
The Client-to-Authenticator Protocol -- the wire format the browser speaks to a roaming authenticator over USB-HID, NFC, or BLE. CTAP1 (the original U2F messages) carries APDU-style binary structures; CTAP2 carries CBOR-encoded commands. A CTAP2 authenticator (also called a FIDO2 or WebAuthn authenticator) implements the CTAP2 command set; modern keys also implement CTAP1 for backwards compatibility [35].
The CTAP2 command-byte table is the surface a browser actually dispatches to. Each command is a single byte followed by a CBOR-encoded request map. The table below names the commands in order and the criterion-table cell each one strengthens.
| Command byte | Command name | What it does | Criterion strengthened |
|---|---|---|---|
| 0x01 | authenticatorMakeCredential | Registration: generate a fresh keypair bound to (rpId, user.id) | Phishing resistance (origin binding) |
| 0x02 | authenticatorGetAssertion | Authentication: sign the challenge with the credential's private key | Phishing + replay + verifier-compromise |
| 0x04 | authenticatorGetInfo | Capability discovery: list supported algorithms, extensions, transports, UV modes | Step-up (lets RP know what's available) |
| 0x06 | authenticatorClientPIN | Manage the PIN, issue pinUvAuthToken with permissions bitmap and rpId scoping | Step-up + replay |
| 0x07 | authenticatorReset | Wipe all resident credentials on the device | Lifecycle |
| 0x09 | authenticatorBioEnrollment | On-token fingerprint enrolment (CTAP 2.1) | Step-up (UV=1) |
| 0x0A | authenticatorCredentialManagement | List, enumerate, and delete resident credentials per RP | Lifecycle / recovery |
| 0x0B | authenticatorSelection | "Pick this device" prompt when multiple authenticators are present | UX (no criterion change) |
| 0x0C | authenticatorLargeBlobs | Per-credential blob store under the credential | Step-up (extension data) |
| 0x0D | authenticatorConfig | Enable enterprise attestation, toggle alwaysUv, set minimum PIN length | Verifier-compromise + lifecycle |
Three pieces of CTAP 2.1 are worth pulling out because they meaningfully change the criteria-table cells.
pinUvAuthToken and permissions. CTAP 2.0's PIN-protocol let the browser obtain a pinAuthToken and use it across any command. CTAP 2.1 introduced a permissions bitmap and rpId scoping on the token so that a token issued for one relying party's ceremony cannot be replayed against a different relying party's ceremony on the same authenticator [39]. This closes a class of host-side mischief: an attacker who got the PIN out of one ceremony could not previously be stopped from spending it on a different rpId.
credProtect. A new extension that lets the RP request a higher protection level on the resident credential -- specifically, that the authenticator should refuse to list the credential without a UV=1 gesture. The first generation of WebAuthn discoverable credentials were enumerable by any host that could speak CTAP2 to the connected key; credProtect lets the RP say "don't show this credential's existence to anything that doesn't pass user verification first."
Enterprise attestation. CTAP 2.1 added an explicit enterprise attestation mode in which the authenticator binds its attestation statement to a list of relying parties the device's enrolling organisation has pre-approved. This is the bridge that makes vendor attestation useful in managed enterprises without leaking the user's specific device identity to every relying party.
The largeBlob extension (CTAP 2.1, command 0x0C) gives each credential a small per-credential blob store. RPs use it for things like cached short-lived tokens or per-user policy. The 2024 release notes for the Windowswebauthn.dll API surface flagged largeBlob support as one of the additions in Windows 11 22H2 [37]; a March 2023 Review Draft [42] foreshadowed the 2.2 refinements that landed in July 2025.
All of this is for experts. When did this stop being a security-team conversation and start being a consumer product? What changed in May 2022?
7. Passkeys: the productisation moment
5 May 2022. Apple, Google, and Microsoft jointly committed at the FIDO Alliance to a common passwordless sign-in standard [43]. The press release is short on protocol detail and long on user-facing language. The headline commitment, verbatim: "Allow users to automatically access their FIDO sign-in credentials (referred to by some as a 'passkey') on many of their devices, even new ones, without having to reenroll every account" [43]. Passkey entered the public lexicon. Andrew Shikiar, the FIDO Alliance's executive director and CMO at the time, named it in the press call.
"Allow users to automatically access their FIDO sign-in credentials (referred to by some as a 'passkey') on many of their devices, even new ones, without having to reenroll every account." -- Apple, Google, and Microsoft, joint FIDO Alliance announcement, 5 May 2022 [43]
The cryptographic move in May 2022 was small. The protocol bytes are the same FIDO2 / WebAuthn / CTAP2 bytes that shipped in March 2019. What changed was twofold: (a) the three platform vendors aligned their sync fabrics so that a passkey created on a user's phone would appear on the user's laptop, and (b) the user-facing terminology consolidated from a confusing menagerie ("discoverable credential," "resident key," "client-side discoverable credential") onto a single product term -- passkey.
A WebAuthn credential whose user.id and account metadata are stored on the authenticator, so the authenticator can produce an assertion without the relying party first supplying a credential identifier. The CTAP 2.0 spec calls these resident keys [35]; the WebAuthn Level 2 spec calls them client-side discoverable credentials [44]; the May 2022 vendor commitment rebranded them as passkeys [43]. All three terms refer to the same on-the-wire object.
Discoverable credentials unlock usernameless sign-in. The relying party does not need to tell the authenticator which credential to use; the authenticator looks up its own resident credentials for the supplied rpId, shows the user the matching account, and asks for the user-verification gesture. This is the UX primitive every consumer-passkey flow leans on.
WebAuthn Level 3 (W3C Candidate Recommendation, latest snapshot dated 13 January 2026 [19] [18]) is the spec generation that productises passkeys. Level 3 standardises:
- The hybrid transport (formerly known as caBLE), §6.3.3 of the L3 spec, which lets a phone act as a roaming authenticator for a nearby laptop via QR code plus ephemeral ECDH plus BLE proximity. We cover hybrid in §12.
- JSON-serialisation helpers --
PublicKeyCredentialCreationOptionsJSONandPublicKeyCredentialRequestOptionsJSON-- that make WebAuthn easier to drive from a server SDK without manual base64url juggling. getClientCapabilities()so the relying party can probe what the client supports before issuing the ceremony.- The
credProps,prf,largeBlob,credProtect, and Secure Payment Confirmation extensions, each of which sharpens one cell of the criteria table.
The mid-2025 cadence picked up: CTAP 2.2 Proposed Standard on 14 July 2025 [40] refined hybrid-transport semantics and tightened credProtect.
The synced-vs-bound distinction is the structural new thing about passkeys. Before May 2022 a FIDO2 credential lived in one secure element; lose the YubiKey, lose the credential. Synced passkeys put the private key into a sync fabric -- Apple iCloud Keychain (originally 2013) [45], Google Password Manager (Chrome password sync, late 2000s onward), Microsoft Authenticator (originally 2015) [46], and Microsoft Account passkey sync (general availability for consumer accounts on 2 May 2024) [47] -- and let it appear on every device the user signs into. The mechanism is end-to-end encryption against a sync-fabric key that the platform vendor cannot read; Apple's Advanced Data Protection model is the strongest current public realisation [48].
The price: the long-term private key has left the original authenticator. NIST is unambiguous about the consequence. The April 2024 supplement Incorporating Syncable Authenticators into NIST SP 800-63B [49] -- since absorbed into NIST SP 800-63B-4 final, July 2025 [4] -- classifies synced passkeys at AAL2, not AAL3, because the key is no longer pinned to a single tamper-resistant element. Yubico's commentary captures the dichotomy verbatim: "FIDO passkeys that are not synced -- device-bound passkeys like YubiKeys -- and are properly stored in dedicated hardware have an AAL3 rating" [8].
The WebAuthn spec made the distinction observable. Two new flag bits in authenticatorData -- BE (Backup Eligible) and BS (Backup State) -- tell the relying party whether the credential is in principle syncable (BE=1) and whether it is currently backed up (BS=1) [19]. The RP can decide policy from those flags: a banking RP can require BE=0 (device-bound) credentials for AAL3 transactions, while accepting BS=1 (synced) credentials for AAL2 sign-in.
Microsoft's own numbers tell the productisation story in raw counts. The May 2024 Microsoft Security blog announcing passkey support for consumer accounts notes that Microsoft was "detecting around 115 password attacks per second" when Windows Hello first shipped in 2015; "less than a decade later, that number has surged 3,378% to more than 4,000 password attacks per second" [47]. The 1 May 2025 World Passkey Day post escalates again: "we observed a staggering 7,000 password attacks per second (more than double the rate from 2023). [...] now we see nearly a million passkeys registered every day." It also reports that "passkey sign-ins are eight times faster than a password and multifactor authentication," and that "more than 99% of people who sign into their Windows devices with their Microsoft account do so using Windows Hello" [50].
Passkeys are not a new cryptographic primitive. They are a productisation moment in which discoverable credentials became consumer-grade UX. The protocol moves were two years earlier; the product move is what changed the criteria-table scoreboard.
Passkeys are a productisation moment. On Windows specifically, what does the platform actually do between navigator.credentials.create and the TPM?
8. The Windows platform authenticator: webauthn.dll end-to-end
May 2019. Windows 10 version 1903. The Win32 platform WebAuthn API shipped, and from that moment on every browser and every native application on Windows that wants to do WebAuthn calls webauthn.dll. The header file webauthn.h is in the Windows SDK and is also published on GitHub at github.com/microsoft/webauthn [51]. The reference page on Microsoft Learn enumerates every function the API surfaces [52]. The 1903 ship date and the subsequent feature additions are documented verbatim by Microsoft Learn: "Microsoft has long been a proponent of passwordless authentication, and has introduced the W3C/Fast IDentity Online 2 (FIDO2) Win32 WebAuthn platform APIs in Windows 10 (version 1903). Starting in Windows 11, version 22H2, WebAuthn APIs support ECC algorithms and starting in Windows 11 version 24H2 WebAuthn APIs support plugin passkey managers" [37].
"When these APIs are in use, Windows 10 browsers or applications don't have direct access to the FIDO2 transports for FIDO-related messaging." -- Microsoft Learn, WebAuthn APIs for password-less authentication on Windows [37]
That sentence is the entire architectural premise. The OS dispatches FIDO2 ceremonies. The browser does not own the CTAP2 stack, the USB-HID transport, the NFC reader, the BLE pairing, or the Hello UV gesture. It hands webauthn.dll a request and gets back an assertion.
The API surface is a small set of functions. The ceremony surface is two functions, the management surface is the remainder.
WebAuthNAuthenticatorMakeCredential-- the registration entry point. Caller supplies origin /rpId/ user / algorithms / attestation preference / authenticator-selection criteria. Returns an attestation object.WebAuthNAuthenticatorGetAssertion-- the authentication entry point. Caller supplies origin /rpId/ allowed credential IDs (or empty for usernameless) / user-verification preference / mediation (Conditional UI, see §9). Returns an assertion.WebAuthNGetApiVersionNumber-- a monotonically increasing integer that lets callers feature-detect. Version 1 is Windows 10 1903; versions step up as Windows adds ECC algorithms (22H2), the plugin model (24H2), and the EXPERIMENTAL_*2 surface (Insider builds via KB5072046 [51]).WebAuthNGetCancellationId/WebAuthNCancelCurrentOperation-- cooperative cancellation; the browser askswebauthn.dllto drop the active ceremony.WebAuthNGetPlatformCredentialList/WebAuthNDeletePlatformCredential-- resident-credential management for synced passkeys held by the OS provider.WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable-- theisUVPAAcapability probe; the RP uses this to decide whether to offer a passkey enrolment flow at all.WebAuthNFreeAssertion/WebAuthNFreeCredentialAttestation/WebAuthNFreePlatformCredentialList-- caller-side memory release; the OS allocates on the heap and the caller is responsible forFree.WebAuthNGetErrorName/WebAuthNGetW3CExceptionDOMError-- translate the Win32HRESULTinto a WebAuthn-spec error string.
Diagram source
flowchart TD
A[Browser or native app] --> B[webauthn.dll: WebAuthNAuthenticatorMakeCredential]
B --> C[Windows Hello UI: prompt for PIN, fingerprint, or face]
C --> D[Windows Hello / Hello for Business: verify gesture]
D --> E[CNG NCRYPT: keypair generation request]
E --> F[TPM 2.0: generate keypair inside the TPM]
F --> G[TPM 2.0: TPM2_Certify over the new credential public key]
G --> H[webauthn.dll: build attestation object with packed or tpm format]
H --> B
B --> A
A --> I[Relying party: verify attestation, store credential public key] The criteria-framework consequence of that call graph is that the private key never leaves the TPM. Microsoft Learn states the property verbatim: "The private keys can only be used after they're unlocked by the user using the Windows Hello unlock factor (biometrics or PIN)" [53]. The TPM enforces use through its own access-control rules; even kernel malware on the host cannot exfiltrate the raw private key, only request operations gated on the user's gesture. This is what gets a Windows-platform-bound passkey on a TPM to AAL3 even when synced passkeys are bounded at AAL2.
The API version sentinel tells a clean feature-evolution story.
| Windows release | API version (approx.) | Notable additions |
|---|---|---|
| Windows 10 1903 (May 2019) | v1 | Initial Win32 surface: make/get credential, isUVPAA |
| Windows 10 1909 / 20H1 | v2 | UV preference, signal-handling refinements |
| Windows 11 21H2 (Oct 2021) | v3 | Hybrid transport (caBLE) entrypoints |
| Windows 11 22H2 (Sep 2022) | v4-v5 | ECC algorithms (ECDSA P-256 platform credentials), Conditional UI mediation |
| Windows 11 23H2 (Oct 2023) | v6 | largeBlob, credProps, refined cancellation |
| Windows 11 24H2 (Oct 2024) | v7 | Plug-in passkey managers (WebAuthNPlugin*), redesigned Hello UX |
| Insider builds (KB5072046) | v7+ | EXPERIMENTAL_WebAuthNPluginAddAuthenticator2, EXPERIMENTAL_WebAuthNPluginPerformUserVerification2, EXPERIMENTAL_WebAuthNPluginUpdateAuthenticatorDetails2 [51] |
EXPERIMENTAL_*2 APIs in github.com/microsoft/webauthn are Insider-only and will lose the EXPERIMENTAL_ prefix as they stabilise. The naming convention is the standard Windows SDK signal for "we want feedback before this becomes load-bearing public API."
The criterion-table consequence of dispatching FIDO2 through one OS surface is that every browser is automatically as strong as the OS. Edge does not need its own attestation logic; neither does Chrome, Firefox, or Brave. They all call the same webauthn.dll, which routes the registration to the TPM (for platform-bound passkeys), to USB-HID (for roaming security keys), or to a plug-in (for Windows 11 24H2 third-party providers, §10).
The webauthn.dll surface answers one half of the question. The other half is: what does the user actually see?
// Origin binding is computationally trivial. The value is in the binding,
// not the cryptography. This snippet computes SHA-256 of an origin's
// effective rpId and compares against the rpIdHash a real authenticator
// would have signed. Paste in a clientDataJSON.origin and the
// authenticatorData.rpIdHash from the earlier snippet to verify.
async function rpIdHash(rpId) {
const enc = new TextEncoder().encode(rpId);
const hash = await crypto.subtle.digest("SHA-256", enc);
return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2,'0')).join('');
}
(async () => {
const goodOrigin = "login.microsoftonline.example";
const badOrigin = "login-microsoft0nline.example";
const goodRpId = "login.microsoftonline.example";
const badRpId = "login-microsoft0nline.example";
console.log("rpIdHash(", goodRpId, ") =", await rpIdHash(goodRpId));
console.log("rpIdHash(", badRpId, ") =", await rpIdHash(badRpId));
// The two hashes differ in every byte. A passkey registered against
// login.microsoftonline.example cannot be induced to sign for the look-alike
// because the authenticator computes the second hash from clientDataJSON.origin
// and refuses to use the credential bound to the first one.
// Replay resistance illustration: a signCount of 0x10 followed by 0x0F
// is illegal (counter regressed). RPs reject this for BS=0 credentials.
const oldCount = 0x10, newCount = 0x0F;
console.log("signCount regression (BS=0)?", newCount <= oldCount ? "REJECT" : "ACCEPT");
})(); Press Run to execute.
9. Conditional UI: passkey autofill that looks like password autofill
The bridge between users' password-trained mental model and the new asymmetric-crypto reality is a UX primitive called Conditional Mediation -- the spec name -- or Conditional UI in informal use. The relying party renders a normal-looking username field. The browser sees that the page has called navigator.credentials.get({mediation: "conditional", publicKey: {...}}) and quietly offers the user's passkey as one of the autofill suggestions, alongside whatever the user has typed and whatever the password manager remembers. The user clicks the passkey suggestion, completes a Windows Hello gesture, and they are signed in. No popup. No modal. No "do you want to use a passkey?" dialog.
A WebAuthn invocation mode in which the browser offers the user's discoverable credentials inside the same autofill UI it uses for saved passwords, rather than via a modal credential picker. The relying party calls navigator.credentials.get({mediation: "conditional", publicKey: {...}}); the browser silently consults the platform authenticator (and, on Windows 11 24H2, the plug-in passkey providers) for credentials matching the rpId. The capability is probed via PublicKeyCredential.isConditionalMediationAvailable() [19].
The canonical engineer-perspective walkthrough is Adam Langley's Passkeys post on imperialviolet.org, dated 22 September 2022 [55]. Langley walks the flag-page invocation needed on early Chrome Canary builds -- chrome://flags#webauthn-conditional-ui -- and the capability surface: isUserVerifyingPlatformAuthenticatorAvailable() to decide whether to offer enrolment, isConditionalMediationAvailable() to decide whether to render the autofill hint at all. The post is the first time most working engineers saw what passkeys would actually look like at the page level.
On Windows the browser calls WebAuthNAuthenticatorGetAssertion with the Conditional mediation flag set; webauthn.dll consults its resident credential store, finds passkeys matching the rpId, and surfaces a small in-line affordance for each. The full-screen Windows Hello modal becomes a small in-place gesture acquisition. From the user's perspective the password-manager metaphor is unchanged; from the cryptography's perspective the work product is a public-key signature over an origin-bound challenge.
The L3 spec section 5.1.4 is the normative reference for the mediation modes [19]. The four modes are: silent (no user interaction), optional (browser decides), conditional (autofill), and required (modal). Conditional is the one that makes passkeys feel like passwords -- and that is precisely why it took the consumer-passkey rollout off the security-team conversation and into product reviews.
The Microsoft Learn passkey overview ties the UX to the Windows ship vehicle: "Starting in Windows 11, version 22H2 with KB5030310, Windows provides a native experience for passkey management" [53]. The Settings -> Accounts -> Passkeys page is the management UI; Conditional Mediation surfaces those passkeys at sign-in time. The passkeys.dev developer directory [56] is the FIDO Alliance's collected resource for relying parties implementing the flow.
The UX implication is the one Adam Langley underlined in the September 2022 post: the password-autofill metaphor is the load-bearing UX primitive that makes passkeys consumer-ready. The cryptography was solved in 2014. The UX took eight more years.
But what if the user's passkey lives in 1Password or Bitwarden, not in Windows itself?
10. The Windows 11 24H2 third-party passkey provider model
8 October 2024. Microsoft published the Windows Developer Blog post Passkeys on Windows: authenticate seamlessly with passkey providers [57] as a pre-conference announcement ahead of the FIDO Alliance's Authenticate 2024 conference (14-16 October 2024 in Carlsbad, California). The post announced three deliverables: "1. A plug-in model for third-party passkey providers. 2. Enhanced native UX for passkeys. 3. A Microsoft synced passkey provider." 1Password and Bitwarden were the named launch partners; Dashlane joined the roster shortly thereafter. The post says verbatim: "Microsoft is partnering closely with 1Password, Bitwarden and others on integrating this capability" [57].
The plug-in model is the first OS-level passkey-provider API on a major desktop platform. macOS Sonoma and iOS 17 had shipped a parallel design (ASCredentialIdentityStore plus ASCredentialProviderExtension) [58]; Android 14 had added Credential Manager support [59]; Windows 11 24H2 is the desktop OS that matches the mobile platforms. The mechanism is a COM interface called IPluginAuthenticator, declared in pluginauthenticator.idl [51]. A passkey-manager vendor ships a packaged Windows app that registers a COM object implementing the interface, supplies an AAGUID and a friendly name, and lets the OS dispatch ceremonies to it.
The Plugin API surface is six functions on the OS side and one COM interface on the vendor side. From webauthnplugin.h and the Microsoft Learn reference [37]:
WebAuthNPluginAddAuthenticator-- register the plug-in with the OS. The vendor app calls this on first run.WebAuthNPluginAuthenticatorAddCredentials-- supply the OS with the credentials the plug-in currently has, so the OS can render them in pickers.WebAuthNPluginAuthenticatorRemoveCredentials-- the inverse; remove credentials the plug-in no longer holds.WebAuthNPluginPerformUserVerification-- request Windows Hello UV on behalf of the plug-in. The plug-in does not take the UV gesture itself; Windows Hello does, so the gesture-to-credential trust path is OS-mediated.WebAuthNPluginRemoveAuthenticator-- the vendor's uninstall path.WebAuthNPluginGetAuthenticatorList-- enumerate which plug-ins the OS knows about.
Three additional EXPERIMENTAL_*2 functions ship in Insider build KB5072046 and refine the registration, UV, and update flows. The list, verbatim from the github.com/microsoft/webauthn README: EXPERIMENTAL_WebAuthNPluginAddAuthenticator2, EXPERIMENTAL_WebAuthNPluginPerformUserVerification2, EXPERIMENTAL_WebAuthNPluginUpdateAuthenticatorDetails2 [51].
Diagram source
flowchart TD
A[Browser or native app] --> B[webauthn.dll]
B --> C{"Provider picker"}
C -->|Windows Hello / platform| D[CNG + TPM 2.0]
C -->|Roaming hardware| E[USB-HID / NFC / BLE]
C -->|Third-party plug-in| F[COM: IPluginAuthenticator]
F --> G[1Password / Bitwarden / Dashlane vault]
F --> H[WebAuthNPluginPerformUserVerification]
H --> I[Windows Hello UI]
I --> H
G --> F
F --> B
B --> A The user-facing flow follows the same logic as the macOS / iOS / Android equivalents. The user installs 1Password or Bitwarden from the Microsoft Store. The vendor app calls WebAuthNPluginAddAuthenticator on first launch. The user enables the provider in Settings -> Accounts -> Passkeys -> Advanced options [57]. From that point on, when any browser or native app on Windows starts a WebAuthn ceremony, webauthn.dll presents the user with a picker -- "use a passkey from Windows Hello, from 1Password, from Bitwarden, from a hardware security key, or from your phone" -- and routes the ceremony to the selected provider. The plug-in itself returns an attestation object and an assertion; Windows Hello handles user verification on the plug-in's behalf via WebAuthNPluginPerformUserVerification. The Windows trust boundary still owns the gesture acquisition.
The criterion-table consequence is mixed. The plug-in model strengthens user choice and recovery, because a user with an existing 1Password / Bitwarden vault can reuse the recovery primitives they already know. It weakens verifier-compromise resistance relative to a pure platform-bound passkey, because the long-term key now lives in the vendor's vault rather than the TPM -- and the vendor's vault becomes another point of compromise. It does not change phishing resistance, replay resistance, or step-up, because those are properties of the WebAuthn ceremony and the plug-in still produces a WebAuthn-shaped assertion.
What 1Password, Bitwarden, and Dashlane each ship in their plug-in implementations follows the same template: registration requests get either a packed attestation statement (for vendor-signed batch attestation keys) or a none attestation (most consumer flows), and authentication assertions come back the same shape as any other WebAuthn assertion. The plug-in itself decides whether the credential is BE=1, BS=1 (synced in the vendor's cloud) or BE=0, BS=0 (device-bound to the local install).
A plug-in supplies the credential. But the attestation statement on registration tells the relying party what kind of credential it is. That's a separate API surface -- what shapes does it come in?
11. The seven attestation conveyance formats
The IANA WebAuthn registry lists seven format identifiers for the attestation statement a registration ceremony can produce [62]. The registry is reachable via RFC 8809 (Hodges, Mandyam, M.B. Jones, August 2020) [63] and the canonical normative definitions are in WebAuthn Level 2 §§8.2-8.8 [44], whose dated Recommendation is at REC-webauthn-2-20210408 [64]. The seven, in registry order: packed, tpm, android-key, android-safetynet, fido-u2f, apple, and none. Each is one option a relying party can require, accept, or ignore.
The mechanism by which a WebAuthn registration ceremony optionally produces a signature over the new credential's public key (and authenticatorData containing the rpIdHash), chained to a vendor or platform root. The relying party validates the chain to establish that the new credential's private key is held by a specific authenticator model or certification level. Attestation is distinct from authentication; attestation runs once at registration, authentication runs every sign-in. The WebAuthn attestation parameter on registration controls whether the RP asks for an attestation statement at all (values: none, indirect, direct, enterprise).
The table below summarises what each format teaches the relying party.
| Format | What the RP verifies | Trust anchor required | Criterion strengthened | Current adoption |
|---|---|---|---|---|
packed | Signature over authenticatorData || clientDataHash by batch attestation key or self-attestation key | Vendor X.509 cert chain or none (self) | Verifier-compromise (model identity), optional anti-fraud | Default for most CTAP2 keys; dominant in production |
tpm | TPM 2.0 TPM2_Certify-style quote over the new credential public key | AIK / EK chain to TPM vendor root | Verifier-compromise + device-bound storage | Windows platform-bound passkeys |
android-key | Android Keystore attestation chain | Google-rooted hardware-attestation CA | Verifier-compromise + StrongBox / TEE residency | Android platform passkeys |
android-safetynet | SafetyNet API-derived attestation token | Google SafetyNet CA | Legacy; declining | Legacy Android; SafetyNet deprecation announced June 2022; migration deadline end of January 2024; complete shutdown end of January 2025 |
fido-u2f | ECDSA P-256 signature with vendor X.509 cert | Vendor U2F-era cert | Verifier-compromise (legacy) | Legacy U2F-era hardware keys; declining |
apple | Anonymous Apple-issued attestation chain | Apple anonymous-attestation CA | Verifier-compromise without device de-anonymisation | Apple platform passkeys |
none | No attestation; credential public key plus AAGUID only | None | None | The default for synced-passkey consumer flows |
A few of these deserve a paragraph each.
packed is the spec default and the most widely deployed. The authenticator emits one signature over the concatenation of authenticatorData and a hash of clientDataJSON, using one of three keys: (a) a per-authenticator-model batch attestation key whose X.509 chain anchors to the vendor's attestation root (the privacy-vs-anti-fraud trade-off -- the cert reveals the device model, but not which specific user owns which device); (b) an Anonymisation CA or Enterprise Attestation key, which lets a managed enterprise distinguish its own devices without leaking that information to consumer relying parties; or (c) a self-attestation key derived from the credential itself, which proves only that the private key signs and makes no identity claim.
tpm is the format the Windows platform authenticator emits when the user has a TPM 2.0. The signing object is a TPM TPM2_Quote-style structure with the TPM's Attestation Identity Key (AIK), chained back to the TPM vendor's Endorsement Key (EK) root certificate. This is the most cryptographically opinionated attestation in the registry: it proves the credential is held by a specific TPM vendor's part. The Windows TPM article in this series walks the AIK / EK chain end to end.
apple is Apple's anonymous-attestation design. The X.509 chain ends in an Apple anonymous-attestation CA; cryptographically the relying party can verify the cert chain back to Apple's root, but the cert itself is engineered to not reveal the user's specific device. This is the privacy-vs-anti-fraud trade-off resolved in favour of privacy: a relying party gets "this came from a real Apple device" without learning which Apple device.
android-safetynet is the legacy format that lots of installed-base Android passkeys still use. Google announced the SafetyNet Attestation API's deprecation in June 2022 in favour of Play Integrity; the migration deadline was extended to end of January 2024, with complete shutdown landing end of January 2025 [65]. Any new Android passkey registered in 2025 or later uses android-key or none instead. Relying parties with old android-safetynet credentials in their database must accept both formats during the transition window; new credentials use the new path.
fido-u2f is the U2F-era legacy format, descended directly from the December 2014 U2F design [30]. ECDSA P-256 signing key plus a vendor X.509 cert. Modern keys still emit it for U2F-mode CTAP1 ceremonies, but every modern CTAP2 ceremony uses packed instead.
none is the most-deployed format in consumer flows -- and the recommended default for any relying party that does not have a specific anti-fraud requirement. The RP asks for attestation: "none"; the authenticator returns just the credential public key and the AAGUID, with no signature chain. The privacy benefit is real: attestation deanonymises the user's device by model, and a relying party that does not need that information should not collect it. The 2024-2026 best practice is attestation: "none" for consumer passkey flows. NIST SP 800-63B-4 (final) inherits this caution [4].
All seven formats assume the authenticator is on the same device as the browser. What happens when it isn't?
12. Hybrid transport: a phone authenticator for a laptop browser
A user on a borrowed Windows laptop with no Windows passkey signs in to their bank by scanning a QR code with their iPhone. The phone is the authenticator. The laptop is the WebAuthn client. The protocol that ties them together is hybrid transport, formerly known as caBLE (Cloud-Assisted Bluetooth Low Energy), standardised in W3C WebAuthn Level 3 §6.3.3 [19].
A WebAuthn transport in which a roaming authenticator (typically a mobile phone) cooperates with a WebAuthn client on a nearby device (typically a laptop) via three concurrent channels: an out-of-band channel (QR code) for one-time setup, BLE for proximity, and HTTPS to a discoverable cloud tunnel relay for the actual ceremony bytes. The cryptographic binding is an ephemeral ECDH key exchanged through the QR code; the BLE proves proximity, not identity; the tunnel relay carries the encrypted ceremony [18].
The ceremony, simplified: the laptop's browser asks the user to use a phone, generates an ephemeral ECDH keypair, and renders a QR code containing the Tunnel Service URL the phone should connect to, the laptop's ephemeral public key, and a derived HMAC key. The phone's camera scans the QR code and derives a shared secret with the laptop via ECDH. The phone then advertises its presence over BLE, the laptop listens for the BLE beacon to confirm physical proximity, and both endpoints connect to the Tunnel Service URL over HTTPS. From that point on, the laptop and the phone exchange CTAP2 ceremony messages, encrypted under the ECDH-derived key, through the tunnel relay. The phone produces a WebAuthn assertion locally using whatever authenticator is on the phone (the Secure Enclave on iPhone, the Android Keystore on Android), encrypts it for the laptop, and the laptop forwards it to the relying party.
Diagram source
sequenceDiagram
participant U as User
participant L as Laptop browser
participant P as Phone authenticator
participant T as Tunnel Service
participant R as Relying Party
L->>R: navigator.credentials.get
R->>L: PublicKeyCredentialRequestOptions
L->>L: generate ephemeral ECDH keypair
L->>U: display QR code (tunnel URL, ephem pubkey, HMAC seed)
U->>P: scan QR code
P->>P: derive shared secret via ECDH
P->>L: BLE advertisement (proximity proof)
L->>L: confirm BLE advertisement
P->>T: HTTPS connect to tunnel URL
L->>T: HTTPS connect to tunnel URL
T->>L: relay encrypted CTAP2 traffic
T->>P: relay encrypted CTAP2 traffic
P->>U: prompt for user verification
U->>P: present gesture
P->>P: produce WebAuthn assertion (origin-bound)
P->>T: encrypted assertion
T->>L: encrypted assertion
L->>R: assertion
R->>U: signed in The criterion-table consequence is precise. Phishing resistance is preserved because the origin in clientDataJSON is the laptop's actual browser origin, which the phone signs over the same way it would for its own browser. The QR code is the cryptographic binding, not the BLE advertisement; the BLE advertisement is a proximity signal that proves the phone is physically near the laptop, but it does not authenticate the phone. The Tunnel Service is a relay, not a trust anchor; even if the tunnel were compromised, the encrypted ceremony bytes would be unreadable without the ECDH-derived key.
The design is attributed in the WebAuthn L3 spec to the W3C WebAuthn-3 editor masthead -- Jeff Hodges, J.C. Jones, Michael B. Jones, Akshay Kumar, and Emil Lundberg as current editors, with Dirk Balfanz as a previous editor [66]. The original caBLE design and the L3 §6.3.3 productisation were led by Google's Chrome security and Android Identity teams; the canonical reference is W3C WebAuthn Level 3 §6.3.3 itself.
Hybrid transport is the only competitor to the Windows platform authenticator that involves no Windows-side credential storage. The Windows laptop holds nothing -- no key, no recovery state, no cached credential. Every ceremony round-trips to the phone. This is the use case the bank-on-a-borrowed-laptop story illustrates: you can sign in to your accounts on a machine you do not own without leaving a credential behind.
How do other authentication approaches score on the criteria framework?
13. Competing approaches scored against the criteria
The criteria-framework table makes the competitive field legible. Five rows, six competing columns: password alone, password plus SMS-OTP, password plus TOTP, password plus push with number matching, smart card / PIV, and device-bound or synced passkey. The NIST SP 800-63B-4 AAL grading [4] and the NIST syncable-authenticator supplement [49] anchor the right edge of the table; Yubico's commentary corroborates the dichotomy between device-bound (AAL3) and synced (AAL2) passkeys [8].
| Criterion | Password | Password + SMS-OTP | Password + TOTP | Password + Push (number match) | Smart Card / PIV | Device-bound passkey | Synced passkey |
|---|---|---|---|---|---|---|---|
| Phishing resistance | None | None (AitM relays the OTP) | None (AitM relays the TOTP) | Partial (number match defeats most kits) | Strong (origin-bound via TLS client auth) | Strong (rpId binding) | Strong |
| Verifier-compromise resistance | None | None (SMS infra leaks) | Partial (TOTP seed on server) | Partial | Strong (public-key only) | Strong | Strong |
| Replay / relay resistance | None | Weak (OTP relay in 30-60 s) | Weak (TOTP relay in 30 s) | Strong (number match per challenge) | Strong (per-handshake nonce) | Strong (challenge + counter) | Strong |
| Step-up / continuity | None | None | None | Partial | Strong (PIN re-prompt) | Strong (UV=1) | Strong |
| Recovery floor | Reset via SMS | SMS-OTP all the way down | TOTP seed reset via SMS | SMS / password | Admin re-issue | RP-dependent backup key | Sync-fabric recovery (Recovery Key + Recovery Contact) |
| NIST AAL ceiling | AAL1 | AAL2 nominal (SMS-OTP deprecated since 800-63-3 in 2017) | AAL2 | AAL2 | AAL3 | AAL3 | AAL2 |
Push MFA needs a paragraph of nuance. Vanilla push -- "tap to approve" -- is phishable by default because the attacker can simply trigger the push at the moment they have the password, and a fatigued user taps. Number matching (the user types a code shown on the laptop into the phone, or vice versa) defeats most kits because it ties the push to a specific session. Location binding (the push is rejected unless the phone is geographically near the laptop) adds another layer. The net is "partial" phishing resistance -- much better than vanilla push, not as strong as origin binding.
Smart cards and PIV deserve their own paragraph because they are not historically associated with WebAuthn but score well on the criteria. A PIV card with a PIN provides strong phishing resistance via TLS client authentication (origin-bound at the TLS layer), strong verifier-compromise resistance via the public-key model, and strong replay resistance via per-handshake nonces. The weakness is recovery: a lost card requires an administrative reissue, which scales poorly for consumer flows. The companion App Identity in Windows article in this series walks the Windows smart-card stack end to end.
OATH-TOTP is interesting in the criteria table because it is phishing-vulnerable by construction. The TOTP code is the same on the legitimate origin and the look-alike; the AitM kit forwards the code through. Google Authenticator's cloud-sync feature additionally broke the verifier-compromise property in a subtle way: if the user's Google account is compromised, the synced TOTP seeds give the attacker a complete second-factor toolkit [67].
SAML and OIDC federation are not competitors to WebAuthn in the criteria table -- they are transport layers above WebAuthn. A SAML or OIDC identity provider does the WebAuthn ceremony for the user; the IdP then issues a SAML assertion or an OIDC ID token to the relying party. WebAuthn underneath is the strong primitive; SAML and OIDC are the enterprise transport for the resulting assertions.
WebAuthn wins decisively on four of five rows. What's left in row five? The recovery row.
14. Theoretical limits: the corners WebAuthn cannot reach
Even with everything from §§4-12 in place, WebAuthn has corners it cannot defend. The relevant impossibility results are well-known in the protocol literature; they are worth naming because they tell a practitioner where defence-in-depth has to come from.
Coerced consent. WebAuthn cannot distinguish a willing user from a coerced one. The protocol's only signal is "the user performed the gesture" -- a fingerprint, a PIN, a face match. No protocol whose only observable is gesture completion can tell whether the user was free at the moment of the gesture. NIST SP 800-63B-4 does not classify physical coercion among the attacks it defends against [4]; this is a general impossibility, not a WebAuthn-specific weakness.
Kernel-level malware on the client. Malware with kernel privilege on the user's device can race the legitimate user. If the malware can call into webauthn.dll and trigger a Hello UV prompt the user blindly approves, it can extract assertions. The mitigation is TPM-bound keys plus the Hello ESS trustlet (covered in the companion Windows Hello and Credential Guard articles), not WebAuthn itself. WebAuthn protects against network attackers; defending against a kernel-mode attacker on the same device requires the OS's secure-kernel architecture.
Sync-fabric compromise. Compromise of Apple iCloud, Google account recovery, or Microsoft's recovery-key service effectively compromises every passkey held there. Apple's Advanced Data Protection model [48] is the strongest currently-shipped consumer realisation of the end-to-end-encrypted sync invariant, and even it depends on the user retaining their Recovery Contact or Recovery Key in some form. The NIST April 2024 supplement classifies synced passkeys at AAL2 for exactly this reason: the private key leaves the original authenticator [49]. Yubico's commentary makes the practitioner consequence explicit: device-bound is AAL3, synced is AAL2 [8].
Username enumeration and discoverable-credential privacy. Discoverable credentials let an authenticator answer "do you have a credential for this rpId?" without further information. A relying party that asks the question maliciously can enumerate which of its users have set up a passkey. The credProtect extension introduced in CTAP 2.1 [39] requires UV=1 to even list the credential, which closes most of the leak; it is not universally deployed.
Counter-regression false positives on synced passkeys. The per-credential signature counter is per-authenticator. A passkey synced across two devices will see the counter desynchronise between them. WebAuthn L3 §6.1.1 explicitly permits a zero-counter for synced passkeys; relying parties that treat any counter regression as evidence of cloning will produce false positives. Treat counter regression as evidence of cloning only for BS=0 (device-bound) credentials. This is a deployment foot-gun, not a protocol flaw.
Diagram source
flowchart LR
A[rpId binding / origin in clientDataJSON] --> P[Phishing resistance]
B[Public-key model / no shared secret] --> V[Verifier-compromise resistance]
C[Per-RP challenge + signCount + BS=0] --> RR[Replay / relay resistance]
D[UP and UV flags + freshness] --> S[Step-up / continuity]
E[BE / BS flags + sync fabric] --> AV[Availability]
F[Recovery Key + Recovery Contact] --> RC[Recovery]
G[TPM 2.0 / hardware secure element] --> AAL[AAL3 device-bound]
H[End-to-end encrypted sync fabric] --> AAL2[AAL2 synced] These are the protocol limits. The biggest practical limit is one the protocol cannot fix at all -- recovery. The protocol can specify what factor produces the credential at sign-in; it cannot specify what factor produces the credential when the original one is lost. That is the application-layer question every relying party answers differently, and it is the question §17 will land on.
15. Open problems: what's still moving in late 2025 / early 2026
Standardisation is not done. Several major surfaces are still in active draft.
WebAuthn Level 3 is currently a W3C Candidate Recommendation [19]; the dated CR snapshot is 13 January 2026 [18]. The expected progression is Candidate Recommendation to Proposed Recommendation to Recommendation through 2026, with no major spec-breaking changes expected at this point in the process. The active editor masthead is Hodges, J.C. Jones, M.B. Jones, Kumar, and Lundberg [66].
CTAP 2.2 is a FIDO Proposed Standard as of 14 July 2025 [40]; CTAP 2.3 is also listed at FIDO's specifications download page [41]. The 2.2 and 2.3 revisions refine hybrid transport, credProtect, and PIN-protocol handling without breaking 2.1's command-byte table.
Cross-vendor passkey portability. The FIDO Alliance Credential Exchange Protocol (CXP) and Credential Exchange Format (CXF) Working Drafts, dated 3 October 2024 [68], are the standards effort. The draft text identifies the problem: "the transfer of credentials between two different providers has traditionally been an infrequent occurrence... As it becomes more common for users to have multiple credential providers that they use to create and manage credentials, it becomes important to address some of the security concerns with regard to migration" [68]. Apple has signalled CXP-based import for iOS; Bitwarden has signalled support. The likely 2026 trajectory is CXP becoming a Proposed Standard and Windows / Android / iOS implementing it as the OS-level import-export passkeys surface.
Quantum-safe attestation. The IANA COSE algorithm registry (last updated 2026-03-04) currently has no PQC algorithm in WebAuthn-recommended status [38]. ECDSA P-256, EdDSA Ed25519, RSA-PKCS1.5, and RSA-PSS are the registered options, all quantum-breakable in principle. A long-lived TPM AIK signed today is forgeable to a quantum-capable adversary at any future date. The companion Post-Quantum Cryptography on Windows article in this series walks the algorithm-side rollout; the WebAuthn deployment side is open. The most plausible trajectory is ML-DSA (FIPS 204) entering the WebAuthn COSE registry by 2027 and existing TPM AIKs receiving a parallel ML-DSA enrolment.
Standards are still moving. What should a practitioner do today?
16. Practical guide: what to do this week
Six pieces of operational advice, each tied to a primary source.
1. Windows developers: use webauthn.dll, do not roll your own. The Win32 reference at learn.microsoft.com/en-us/windows/win32/api/webauthn/ [52] is the only surface you should be calling. The OS handles USB-HID, NFC, BLE, hybrid transport, Conditional Mediation, plug-in dispatch, and Windows Hello UV in one call. The header is at github.com/microsoft/webauthn [51]; the Microsoft Learn overview is at learn.microsoft.com/.../hello-for-business/webauthn-apis [37].
2. Relying parties: default to attestation: "none", userVerification: "required", residentKey: "preferred". This is the 2024-2026 consumer-flow baseline. attestation: "none" preserves user privacy and interoperates with every authenticator type. userVerification: "required" forces UV=1 and the gesture acquisition. residentKey: "preferred" enables usernameless sign-in on platforms that support it without burning a credential slot on older authenticators that don't. The Microsoft Entra passwordless documentation [2] and the WebAuthn Level 3 spec [19] are the references.
3. Enterprise IT: device-bound FIDO2 keys for AAL3 (admin, finance, tier 0); synced passkeys for AAL2 workforce. NIST SP 800-63B-4 [4] formalises the dichotomy via the syncable-authenticator supplement [49]. Yubico's enterprise commentary makes the operational point: device-bound passkeys on dedicated hardware are AAL3; synced passkeys are AAL2 [8]. For admin accounts use FIDO Alliance L3-certified hardware [5] -- YubiKey Bio, Feitian BioPass, the Entra-listed vendors at learn.microsoft.com/.../concept-fido2-hardware-vendor [36].
4. Windows 11 24H2 end users: enable third-party passkey providers in Settings. Settings -> Accounts -> Passkeys -> Advanced options. Toggle the provider on for any vendor you trust (1Password, Bitwarden, Dashlane) [57]. The Microsoft Learn third-party tutorial walks the flow [61]. If you do not use a third-party vault, the Microsoft synced passkey provider is enabled by default on 24H2 systems signed in with a Microsoft Account.
5. Security architects: write down your recovery flow first. Score it against the five-axis criteria table from §2 before you design the authentication factors. The recovery row's strength is the system's ceiling, not the floor; the authentication ceremony cannot raise it. Microsoft Entra's own guidance flags account recovery as a deployment risk: FIDO2 keys "can increase costs for equipment, training, and helpdesk support -- especially when users lose their physical keys and need account recovery" [2]. §17 lands this argument.
6. Incident responders: collect ETW events from the WebAuthn provider. Plug-in authenticator registration events on managed devices are a high-signal indicator -- a newly enrolled IPluginAuthenticator on a privileged user's machine should be treated as a credential-store change requiring review. The companion ETW on Windows article in this series walks the WebAuthn provider events end to end.
A one-line PowerShell to enumerate Windows passkeys
Open PowerShell as the signed-in user (no admin needed for your own credentials) and call into the webauthn.dll WebAuthNGetPlatformCredentialList API via a managed wrapper, or use the Settings -> Accounts -> Passkeys page directly. There is no first-class Get-WebAuthnCredential cmdlet as of Windows 11 25H2; the Settings page is the supported management surface. The Microsoft Learn passkey overview is the canonical reference [53].
Most of this is engineering. One row of the table has resisted engineering for fifty years. That's where the article lands.
17. Recovery: your weakest factor is always your recovery flow
The thesis surfaced in §2 and deferred through twelve sections is the one the article lands on. The argument is direct, almost embarrassingly so: every authentication system that admits any external recovery primitive is, in the formal sense, at most as strong as that primitive. Strong authentication ceremonies coexist with weaker recovery ceremonies in every consumer platform in production, and the system's assurance level is the minimum of the two, not the maximum.
Your weakest factor is always your recovery flow.
To make the claim concrete, score every major platform's recovery flow against the same five-axis criteria table.
Apple iCloud Keychain (with Advanced Data Protection). Apple's published model has three recovery primitives [48]: (a) a trusted device the user previously signed into; (b) an iCloud Recovery Contact -- another Apple ID owner the user has nominated to attest their identity; and (c) an iCloud Recovery Key -- a 28-character string the user must retain [69]. Apple's published architecture is the strongest current consumer realisation of the end-to-end-encrypted invariant: the recovery primitives unlock an HSM-backed escrow cluster that holds the user's iCloud Keychain encryption material, but Apple itself does not hold the keys in plaintext. The fundamental dependency is the Apple ID password plus, originally, SMS-OTP at device-trust establishment.
Google Password Manager (with Google Account end-to-end encrypted passkey sync). Trusted-device fallback, security-key fallback, recovery code, recovery phone, recovery email. The recovery floor reduces, in the worst case, to SMS-OTP via the recovery phone. Google's architecture is end-to-end encrypted in the steady state but the trust establishment depends on Google account recovery, which depends on out-of-band verification primitives the user enrolled at account creation.
Microsoft Account. The October 2024 Windows Developer Blog states the recovery primitive verbatim: "you will be prompted to save a recovery key that will be used to verify your identity and protect your passkeys through end-to-end encryption" [57]. The recovery key is a high-entropy string the user retains; if they lose it, the recovery flow falls back to the secondary factors the user enrolled (alternate email or SMS-OTP via the recovery phone). As with Google, the worst-case recovery floor is the weakest of the secondary factors the user enrolled.
Microsoft Entra ID (enterprise). Entra's Temporary Access Pass (TAP) is the strongest enterprise recovery primitive currently shipped: an administrator issues a time-bound passwordless TAP that the user redeems to bootstrap a new authenticator. TAP is stronger than consumer flows because of accountability -- the admin's identity is on the issuance -- but weaker than the authentication ceremony itself because the admin is socially engineerable. Microsoft documents the TAP issuance and redemption flow in detail [70].
1Password, Bitwarden, Dashlane under the 24H2 plug-in model. Each vendor's master password and secondary recovery primitive becomes the de facto floor of the entire passkey ceremony when the plug-in is the credential store. 1Password's master password plus Secret Key, Bitwarden's master password plus 2FA recovery code, and Dashlane's device trust plus master password -- each is the recovery floor for every passkey the vault holds. The Microsoft Learn third-party tutorial reinforces the warning, in context: "Contoso Passkey Manager is designed for passkey creation and usage testing only. Don't use the app for production passkeys" [61].
Diagram source
flowchart TD
A[Apple iCloud Keychain ADP] --> A1[Recovery Contact]
A --> A2[Recovery Key 28 chars]
A --> A3[Trusted device]
A3 --> A4[Apple ID password + SMS-OTP at trust establishment]
B[Google Password Manager] --> B1[Recovery code]
B --> B2[Recovery phone]
B --> B3[Recovery email]
B2 --> B4[SMS-OTP]
C[Microsoft Account] --> C1[Recovery Key]
C --> C3[Alternate email]
C --> C4[Recovery phone -> SMS-OTP]
D[Entra ID enterprise] --> D1[Temporary Access Pass]
D1 --> D2[Admin: socially engineerable]
E[1Password / Bitwarden / Dashlane vault] --> E1[Master password + Secret Key / 2FA recovery code]
A4 --> Z[Weak shared-knowledge or SMS-OTP floor]
B4 --> Z
C4 --> Z
D2 --> Z
E1 --> Z The diagram looks busy because it is. Every major platform's recovery flow is a different combination of trusted-device fallback, recovery code or key, recovery contact, and an out-of-band primitive (SMS-OTP, email, or admin attestation). Every one of those out-of-band primitives is weaker than origin-bound public-key cryptography. The cryptographic ceremony scores AAL3 phishing-resistant at the authentication moment; the recovery primitive scores AAL1 or AAL2 at the recovery moment. The system's AAL is the minimum.
Every passkey platform in production in 2026 -- Apple, Google, Microsoft, Entra, 1Password, Dashlane, Bitwarden -- bottoms out, in its recovery flow, in some combination of trusted-device fallback and SMS-OTP-equivalent shared knowledge. That floor is the AAL ceiling for the entire system.
The protocol literature has been clear about this for fifty years and the regulatory literature has been catching up since 2017. NIST SP 800-63-3 introduced "phishing-resistant authenticator" as a first-class term; SP 800-63-4 (2025) [3] makes verifier-impersonation resistance a normative criterion. Neither standard solves recovery; both standards explicitly enumerate what counts as a recovery primitive without specifying how to compose them into an AAL-graded flow. There is no IETF or FIDO Alliance standard that says "here is a recovery flow whose strength is AAL3." There may never be -- recovery is application-specific, and the only general protocol is "social attestation" (multiple human witnesses), which does not scale.
The same WebAuthn ceremony that scores AAL3 phishing-resistant at the authentication moment can be a single-factor SMS-OTP at the recovery moment. Your weakest factor is always your recovery flow. That is the line. It is the line every working security architect should write down, score against, and design recovery against -- before designing the authentication factors.
18. FAQ
Common questions about WebAuthn and passkeys on Windows
Are passkeys just passwords with a fancy name?
No. A password is a shared secret -- the user types a string, the server stores a hash of the same string, and an eavesdropper who captures the string in flight or compromises the server's database has a credential they can replay. A passkey is one half of an asymmetric keypair: the private key lives in the authenticator (TPM, secure enclave, hardware key, or end-to-end-encrypted sync fabric), and only its public key reaches the server. An eavesdropper who captures a passkey ceremony in flight has nothing they can replay; a server-database leak yields public keys that authenticate no one. WebAuthn Level 3 [19] and the Microsoft Entra "origin-bound public key cryptography" framing [2] are the references.
Are synced passkeys insecure?
Insecure relative to device-bound; secure relative to passwords. The NIST syncable-authenticator supplement (April 2024) [49] and SP 800-63B-4 (July 2025) [4] cap synced passkeys at AAL2, because the long-term private key has left the original authenticator. Device-bound passkeys on dedicated hardware -- "FIDO passkeys that are not synced ... and are properly stored in dedicated hardware have an AAL3 rating" [8] -- can reach AAL3. The right answer is to use device-bound keys for tier-zero accounts and synced passkeys for the bulk of consumer flows.
Is Windows Hello a biometric system?
Hello uses biometrics but provides the user-verification gesture for WebAuthn; the credential itself is asymmetric and lives in the TPM. Microsoft Learn states the property verbatim: "The private keys can only be used after they're unlocked by the user using the Windows Hello unlock factor (biometrics or PIN)" [53]. The biometric is one mode of the Hello UV gesture, not the credential. If you disable face or fingerprint, your PIN still unlocks the passkey.
Should relying parties always require attestation?
No. Attestation is privacy-leaking for synced passkeys; attestation: "none" is the 2024-2026 default for consumer flows. Use attestation: "direct" only when you have a documented anti-fraud requirement and can verify the chain against the FIDO Metadata Service. Use attestation: "enterprise" only inside a managed enterprise where the user's device is corporately enrolled. The relevant references are WebAuthn Level 2 §§8.2-8.8 [44] and the IANA WebAuthn registry [62].
Does hybrid transport work because of Bluetooth proximity?
No. The cryptographic binding is the QR-code-encoded ephemeral ECDH key. Bluetooth is a transport and a proximity signal; it is not a trust anchor. The QR code transfers the laptop's ephemeral public key plus a derived HMAC seed; the phone derives the shared secret via ECDH; the BLE advertisement merely proves the phone is physically close to the laptop. The encrypted CTAP2 ceremony bytes travel over HTTPS through a discoverable tunnel relay. WebAuthn Level 3 §6.3.3 is the normative description [19].
Does disabling Windows Hello disable passkeys?
No. A Windows passkey can be used with PIN-only user verification; the biometric is one mode of the Hello UV gesture, not the credential. The credential is in the TPM, indexed under your Microsoft Account container, and the PIN is one valid unlock factor. If you use a third-party passkey provider via the 24H2 plug-in model, that provider may use its own master password as the UV gesture; the OS still mediates the gesture acquisition through WebAuthNPluginPerformUserVerification [37].
Can Microsoft, Apple, or Google see my private keys?
Microsoft cannot see your TPM-sealed Windows Hello private key; the TPM does not expose the raw key material to the OS, let alone to Microsoft. Apple's iCloud Keychain with Advanced Data Protection [48] and Google's end-to-end-encrypted passkey sync mean the sync provider cannot see the plaintext keys either. But the recovery path can still expose them under specific conditions: an attacker who compromises your recovery contact, recovery key, or your account's out-of-band recovery primitives (SMS-OTP, recovery email) effectively defeats the end-to-end encryption invariant. The plaintext keys are not what gets exfiltrated; the recovery primitives are.
This article is one of a series on Windows authentication primitives. NTLMless: The Death of NTLM in Windows (2026-05-10) covers the legacy authentication protocol passkeys are displacing. Windows Hello, Demystified covers the user-verification gesture WebAuthn leans on. Adminless: Administrator Protection in Windows (2026-05-10) and App Identity in Windows (2026-05-08) cover the privilege-escalation and code-identity primitives that surround the authentication stack. The companion Kerberos on Windows (2026-05-11) covers the enterprise transport for the resulting assertions; ETW on Windows (2026-05-11) covers the telemetry surface for incident responders.
The Windows passkey stack is the productisation moment for a forty-year-old protocol-literature insight: authentication should be tied to something the network attacker cannot change. WebAuthn ties it to the origin in clientDataJSON, signed by a credential whose private key never reaches the wire. Windows 10 1903 made it a Win32 surface; Windows 11 24H2 made it a plug-in surface; Authenticate 2024 made it a default. The protocol bytes are FIDO2; the consumer experience is autofill. The Windows part is the dispatcher between them.
The criteria framework is the diagnostic kit. Use it on every authentication system you ship. Score it against five axes, not three. Write down the recovery flow first. Match the authentication ceremony to the recovery flow you can actually defend. And remember the line: your weakest factor is always your recovery flow.
Study guide
Key terms
- Phishing-resistant authenticator
- An authenticator whose protocol prevents a relying party impersonator from inducing the authenticator to release a usable credential value. NIST SP 800-63B-4 calls this verifier-impersonation resistance.
- Origin binding
- The mechanism by which WebAuthn enforces phishing resistance: the browser writes the origin into clientDataJSON; the authenticator signs over the SHA-256 hash of the canonical rpId; the RP rejects any signature whose rpIdHash does not match the registered rpId.
- rpId
- A string identifying the WebAuthn relying party for credential scoping. Must be a registrable suffix of the page's origin. All WebAuthn signatures are made over its SHA-256 hash.
- CTAP 2.x
- The Client-to-Authenticator Protocol: the wire format browser to roaming authenticator over USB-HID, NFC, or BLE. CTAP1 is APDU-based; CTAP2 is CBOR-based. Modern keys speak CTAP 2.1 (June 2021) or 2.2 (July 2025).
- Discoverable credential (resident key, passkey)
- A WebAuthn credential whose account metadata is stored on the authenticator, enabling usernameless sign-in. CTAP 2.0 called these resident keys; the May 2022 vendor commitment branded them passkeys.
- Attestation conveyance
- The mechanism by which a registration ceremony optionally produces a signature over the credential public key, chained to a vendor or platform root. Seven IANA-registered formats: packed, tpm, android-key, android-safetynet, fido-u2f, apple, none.
- Hybrid transport (caBLE)
- A WebAuthn transport in which a phone acts as a roaming authenticator for a nearby laptop. QR code carries an ephemeral ECDH key; BLE proves proximity; HTTPS tunnel relay carries encrypted CTAP2 bytes.
- AAGUID
- A 16-byte Authenticator Attestation GUID identifying the authenticator make and model. Some authenticators emit all-zeros for privacy; the FIDO Metadata Service is the authoritative directory.
- Conditional UI / Conditional Mediation
- A WebAuthn invocation mode in which the browser offers discoverable credentials inside the autofill UI rather than via a modal picker. RP calls navigator.credentials.get with mediation: 'conditional'.
- BE / BS flags
- Backup Eligible and Backup State bits in authenticatorData. BE=1 means the credential is in principle syncable; BS=1 means it is currently backed up. NIST SP 800-63B-4 caps BS=1 credentials at AAL2.
- AAL1 / AAL2 / AAL3
- NIST SP 800-63B-4 authentication assurance levels. AAL1 is single-factor; AAL2 is multi-factor or phishing-resistant; AAL3 is hardware-bound non-syncable authentication.
References
- (2024). Tycoon 2FA: an in-depth analysis of the latest version of the AiTM phishing kit. Sekoia.io. https://blog.sekoia.io/tycoon-2fa-an-in-depth-analysis-of-the-latest-version-of-the-aitm-phishing-kit/ ↩
- Passwordless authentication options for Microsoft Entra ID. Microsoft Learn. https://learn.microsoft.com/en-us/entra/identity/authentication/concept-authentication-passwordless ↩
- (2025). NIST SP 800-63-4: Digital Identity Guidelines (final, July 2025). NIST. https://pages.nist.gov/800-63-4/ - Live canonical HTML landing page (the csrc.nist.gov/pubs/sp/800/63/4/final URL and the nvlpubs PDF mirror both return HTTP 404 as of 2026-05-12). ↩
- (2025). NIST SP 800-63B-4: Authentication and Authenticator Management (HTML). NIST. https://pages.nist.gov/800-63-4/sp800-63b.html ↩
- FIDO Authenticator Certification Levels (L1, L1+, L2, L3, L3+). FIDO Alliance. https://fidoalliance.org/certification/authenticator-certification-levels/ ↩
- (2007). RFC 5056: On the Use of Channel Bindings to Secure Channels. IETF. https://datatracker.ietf.org/doc/html/rfc5056 ↩
- (2022). RFC 9266: Channel Bindings for TLS 1.3. IETF. https://datatracker.ietf.org/doc/html/rfc9266 ↩
- New NIST Guidance on Passkeys: Key Takeaways for Enterprises. Yubico. https://www.yubico.com/blog/new-nist-guidance-on-passkeys-key-takeaways-for-enterprises/ ↩
- (1979). Password Security: A Case History. Communications of the ACM 22(11), 594-597. https://en.wikipedia.org/wiki/Robert_Morris_(cryptographer) ↩
- (2005). RFC 4226: HOTP -- An HMAC-Based One-Time Password Algorithm. IETF. https://datatracker.ietf.org/doc/html/rfc4226 ↩
- (2011). RFC 6238: TOTP -- Time-Based One-Time Password Algorithm. IETF. https://datatracker.ietf.org/doc/html/rfc6238 ↩
- (2018). RFC 8471: The Token Binding Protocol Version 1.0. IETF. https://datatracker.ietf.org/doc/html/rfc8471 ↩
- (2018). RFC 8473: Token Binding over HTTP. IETF. https://datatracker.ietf.org/doc/html/rfc8473 ↩
- RFC 8471 datatracker history page. IETF. https://datatracker.ietf.org/doc/rfc8471/history/ - History shows no Historic reclassification event; RFC remains Proposed Standard. ↩
- RFC 8473 datatracker history page. IETF. https://auth.ietf.org/doc/rfc8473/history/ ↩
- Token Binding (Wikipedia). Wikipedia. https://en.wikipedia.org/wiki/Token_Binding ↩
- (2016). Web Authentication: An API for accessing Scoped Credentials (W3C First Public Working Draft, 31 May 2016). W3C. https://www.w3.org/TR/2016/WD-webauthn-20160531/ ↩
- (2026). Web Authentication Level 3 (Candidate Recommendation dated snapshot, 13 January 2026). W3C. https://www.w3.org/TR/2026/CR-webauthn-3-20260113/ ↩
- Web Authentication: An API for accessing Public Key Credentials -- Level 3 (W3C Candidate Recommendation). W3C. https://www.w3.org/TR/webauthn-3/ ↩
- Trusted Computer System Evaluation Criteria (Orange Book, Wikipedia). Wikipedia. https://en.wikipedia.org/wiki/Trusted_Computer_System_Evaluation_Criteria ↩
- (2004). NIST Special Publication 800-63 (first edition): Electronic Authentication Guideline. NIST CSRC. https://csrc.nist.gov/pubs/sp/800/63/final ↩
- (2004). NIST SP 800-63 version 1.0.2 (PDF). NIST. https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-63ver1.0.2.pdf ↩
- RSA SecurID (Wikipedia). Wikipedia. https://en.wikipedia.org/wiki/RSA_SecurID ↩
- (2017). NIST SP 800-63-3: Digital Identity Guidelines. NIST CSRC. https://csrc.nist.gov/pubs/sp/800/63/3/upd2/final ↩
- (1995). RFC 1760: The S/KEY One-Time Password System. IETF. https://datatracker.ietf.org/doc/html/rfc1760 ↩
- (2005). The Laws of Identity. identityblog.com. https://www.identityblog.com/?p=352 ↩
- (2013). FIDO Alliance Launch Announcement (PDF), 12 February 2013. FIDO Alliance. https://fidoalliance.org/assets/downloads/FIDO_Alliance_launch__FINAL__02_12_13docx.pdf ↩
- FIDO Alliance (Wikipedia). Wikipedia. https://en.wikipedia.org/wiki/FIDO_Alliance ↩
- Mozilla Persona (Wikipedia). Wikipedia. https://en.wikipedia.org/wiki/Mozilla_Persona ↩
- FIDO U2F Overview (specs archive). FIDO Alliance. https://fidoalliance.org/specs/u2f-specs-master/fido-u2f-overview.html ↩
- Universal 2nd Factor (Wikipedia). Wikipedia. https://en.wikipedia.org/wiki/Universal_2nd_Factor ↩
- (2016). Security Keys: Practical Cryptographic Second Factors for the Modern Web. Financial Cryptography 2016. https://fc16.ifca.ai/preproceedings/25_Lang.pdf ↩
- (2019). W3C and FIDO Alliance Finalize Web Standard for Secure, Passwordless Logins. W3C. https://www.w3.org/press-releases/2019/webauthn/ ↩
- (2019). Web Authentication: An API for accessing Public Key Credentials -- Level 1 (W3C Recommendation, 4 March 2019). W3C. https://www.w3.org/TR/2019/REC-webauthn-1-20190304/ ↩
- (2019). FIDO Client to Authenticator Protocol (CTAP) 2.0 Proposed Standard, 30 January 2019. FIDO Alliance. https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html ↩
- FIDO2 security key hardware vendors for Microsoft Entra ID. Microsoft Learn. https://learn.microsoft.com/en-us/entra/identity/authentication/concept-fido2-hardware-vendor ↩
- WebAuthn APIs for password-less authentication on Windows. Microsoft Learn. https://learn.microsoft.com/en-us/windows/security/identity-protection/hello-for-business/webauthn-apis ↩
- CBOR Object Signing and Encryption (COSE) Algorithms (IANA registry). IANA. https://www.iana.org/assignments/cose/cose.xhtml ↩
- (2021). FIDO Client to Authenticator Protocol (CTAP) 2.1 Proposed Standard, 15 June 2021. FIDO Alliance. https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html ↩
- (2025). FIDO CTAP 2.2 Proposed Standard, 14 July 2025. FIDO Alliance. https://fidoalliance.org/specs/fido-v2.2-ps-20250714/fido-client-to-authenticator-protocol-v2.2-ps-20250714.html ↩
- FIDO Alliance Specifications Download Page. FIDO Alliance. https://fidoalliance.org/specifications/download/ ↩
- (2023). FIDO CTAP 2.2 Review Draft, 21 March 2023. FIDO Alliance. https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html ↩
- (2022). Apple, Google and Microsoft Commit to Expanded Support for FIDO Standard to Accelerate Availability of Passwordless Sign-Ins. FIDO Alliance. https://fidoalliance.org/apple-google-and-microsoft-commit-to-expanded-support-for-fido-standard-to-accelerate-availability-of-passwordless-sign-ins/ ↩
- Web Authentication: An API for accessing Public Key Credentials -- Level 2 (latest). W3C. https://www.w3.org/TR/webauthn-2/ ↩
- iCloud Keychain (Wikipedia). Wikipedia. https://en.wikipedia.org/wiki/ICloud_Keychain ↩
- Microsoft Authenticator (Wikipedia). Wikipedia. https://en.wikipedia.org/wiki/Microsoft_Authenticator ↩
- (2024). Microsoft Introduces Passkeys for Consumer Accounts. Microsoft Security Blog. https://www.microsoft.com/en-us/security/blog/2024/05/02/microsoft-introduces-passkeys-for-consumer-accounts/ ↩
- iCloud data security overview (Standard and Advanced Data Protection). Apple Support. https://support.apple.com/en-us/HT202303 ↩
- (2024). NIST SP 800-63B Supplement 1: Incorporating Syncable Authenticators. NIST CSRC. https://csrc.nist.gov/pubs/sp/800/63/b/sup/final ↩
- (2025). Pushing passkeys forward: Microsoft's latest updates for simpler, safer sign-ins. Microsoft Security Blog. https://www.microsoft.com/en-us/security/blog/2025/05/01/pushing-passkeys-forward-microsofts-latest-updates-for-simpler-safer-sign-ins/ ↩
- microsoft/webauthn: Win32 APIs and plugin header files. GitHub. https://github.com/microsoft/webauthn ↩
- webauthn.h Win32 API reference. Microsoft Learn. https://learn.microsoft.com/en-us/windows/win32/api/webauthn/ ↩
- Passkeys (Windows identity protection). Microsoft Learn. https://learn.microsoft.com/en-us/windows/security/identity-protection/passkeys/ ↩
- YubiKey 5 Series Overview. Yubico. https://www.yubico.com/products/yubikey-5-overview/ ↩
- (2022). Passkeys. imperialviolet.org. https://www.imperialviolet.org/2022/09/22/passkeys.html ↩
- passkeys.dev (FIDO Alliance / WebKit / Chrome team passkey resource). FIDO Alliance. https://passkeys.dev/ ↩
- (2024). Passkeys on Windows: authenticate seamlessly with passkey providers. Windows Developer Blog. https://blogs.windows.com/windowsdeveloper/2024/10/08/passkeys-on-windows-authenticate-seamlessly-with-passkey-providers/ ↩
- ASCredentialIdentityStore -- AuthenticationServices. Apple Developer. https://developer.apple.com/documentation/authenticationservices/ascredentialidentitystore ↩
- Credential Manager (Android Developers). Android Developers. https://developer.android.com/identity/credential-manager - Canonical general Credential Manager API doc (Android 14+). ↩
- Contoso Passkey Manager (Windows-classic-samples). GitHub. https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/PasskeyManager ↩
- Third-party passkey providers on Windows. Microsoft Learn. https://learn.microsoft.com/en-us/windows/apps/develop/security/third-party ↩
- WebAuthn Attestation Statement Format Identifiers (IANA registry). IANA. https://www.iana.org/assignments/webauthn/webauthn.xhtml ↩
- (2020). RFC 8809: Registries for Web Authentication (WebAuthn). IETF. https://datatracker.ietf.org/doc/html/rfc8809 ↩
- (2021). Web Authentication: An API for accessing Public Key Credentials -- Level 2 (W3C Recommendation, 8 April 2021). W3C. https://www.w3.org/TR/2021/REC-webauthn-2-20210408/ ↩
- (2024). SafetyNet Attestation API deprecation timeline. Android Developers. https://developer.android.com/privacy-and-security/safetynet/attestation - Auto-redirects to the canonical .../deprecation-timeline page (the original android-developers.googleblog.com 2022 post body now returns a Blogger error template). ↩
- WebAuthn (Wikipedia). Wikipedia. https://en.wikipedia.org/wiki/WebAuthn ↩
- Google Authenticator (Wikipedia). Wikipedia. https://en.wikipedia.org/wiki/Google_Authenticator ↩
- (2024). FIDO Credential Exchange Protocol (CXP) v1.0 Working Draft, 3 October 2024. FIDO Alliance. https://fidoalliance.org/specs/cx/cxp-v1.0-wd-20241003.html ↩
- Use a recovery key for your Apple Account. Apple Support. https://support.apple.com/en-us/109345 ↩
- Configure Temporary Access Pass to register passwordless authentication methods. Microsoft Learn. https://learn.microsoft.com/en-us/entra/identity/authentication/howto-authentication-temporary-access-pass ↩