Passkeys and security keys are only phishing-resistant if the relying party verifies what the protocol gives it, and most WebAuthn weaknesses in practice are verification and policy mistakes on the server. FIDO2 is the combination of the W3C Web Authentication API (WebAuthn), which browsers and platforms expose to websites, and the FIDO Alliance Client to Authenticator Protocol (CTAP), which clients use to talk to roaming authenticators. WebAuthn Level 3 became a W3C Recommendation on 25 August 2026, bringing backup flags, related origins, the PRF extension and client capability detection into the stable standard. This guide explains the internals a relying party engineer needs: the registration and authentication ceremonies, the authenticator data and flags, attestation and the FIDO Metadata Service, and the server checks that make or break security. It is for identity engineers, application security reviewers and developers implementing or auditing a WebAuthn server. Rollout and user experience are covered in the Passwordless and Passkeys Guide and the Passkey Rollout Guide.
Key takeaways
- Origin binding is the security property: the browser writes the calling origin into clientDataJSON and the authenticator scopes the credential to an RP ID, so a phishing site cannot obtain a usable assertion; the server must still check both.
- The flags carry policy: UP (user present), UV (user verified), BE (backup eligible) and BS (backed up) tell the server what kind of credential and gesture it got; ignoring them silently downgrades assurance.
- Attestation is optional and often absent: synced passkeys usually return no attestation; use attestation and the FIDO Metadata Service only where you must restrict authenticator models, such as high-assurance or regulated access.
- Challenges must be single-use and server-held: a reused, predictable or client-generated challenge turns WebAuthn into a replayable signature.
- Level 3 is now stable: backup state, related origin requests, the prf and largeBlob extensions and getClientCapabilities() are in the W3C Recommendation, so they can be specified in requirements rather than treated as experimental.
The FIDO2 stack: who does what
| Component | Specification | Role | Current status (checked 27 Oct 2026) |
|---|---|---|---|
| Relying party (RP) server | WebAuthn section on RP operations | Generates challenges, stores credential records, verifies responses, sets policy | WebAuthn Level 3, W3C Recommendation, 25 August 2026 |
| Client (browser or OS platform) | WebAuthn API: navigator.credentials.create() and get() | Checks the RP ID against the origin, builds clientDataJSON, mediates UI | As above |
| Roaming authenticator (security key, phone) | CTAP 2.x over USB, NFC, BLE or hybrid | Holds keys, performs user presence and verification, signs | CTAP 2.2 and 2.3 Proposed Standards; 2.3.1 working draft |
| Platform authenticator | Vendor implementation behind WebAuthn | Keys in TPM, Secure Enclave or OS keystore, often synced as passkeys | Vendor-specific |
| Metadata | FIDO Metadata Service and Metadata Statements 3.x | Signed registry of certified authenticator models | 3.1.1 Proposed Standard, January 2026 |
CTAP versions matter for roaming authenticators: CTAP 2.1 introduced features such as credential management and enterprise attestation that many deployed keys support, and later versions extend them. Check which version your approved security keys implement before requiring a feature. See the Hardware Security Keys Guide.
The registration ceremony
- Server builds options: a random challenge (at least 16 bytes, stored server-side and bound to the session), the RP ID and name, a user entity with an opaque user handle (at most 64 bytes and containing no personal information, per Level 3), the list of acceptable public key algorithms (for example COSE -7 ES256 and -8 EdDSA, with -257 RS256 for some platform authenticators), excludeCredentials to stop duplicate registration, and authenticatorSelection (residentKey and userVerification).
- Client checks the RP ID: the RP ID must be the caller's effective domain or a registrable suffix of it, unless the client supports related origin requests and the RP's
/.well-known/webauthnfile lists the caller. - Authenticator creates a key pair: after a user presence test and, if requested, user verification (PIN or biometric), it creates a credential scoped to the RP ID and returns authenticator data with attested credential data, plus an attestation statement.
- Client returns: clientDataJSON (type
webauthn.create, the base64url challenge, the origin, crossOrigin and, where relevant, topOrigin) and the attestation object. - Server verifies and stores: see the verification table below, then stores a credential record: credential ID (at most 1023 bytes), public key, signature counter, transports, uvInitialized, backupEligible and backupState.
Option values that set assurance
| Option | Values | Effect and guidance |
|---|---|---|
| residentKey | required, preferred, discouraged | required creates a discoverable credential (a passkey) that can sign in without a username; needed for usernameless flows |
| userVerification | required, preferred, discouraged | required makes the ceremony fail if the authenticator cannot verify the user; use it whenever the passkey is the only factor |
| attestation | none, indirect, direct, enterprise | none is the privacy-friendly default; direct requests the authenticator's statement; enterprise returns uniquely identifying attestation only where the user agent permits it for that RP ID |
| authenticatorAttachment and hints | platform or cross-platform; hints such as security-key, client-device, hybrid | Steer the UI towards a type of authenticator; they are preferences, not enforcement |
The authentication ceremony
- Server issues a challenge and either an allowCredentials list (username-first) or an empty list for discoverable credentials, in which case the authenticator returns the user handle.
- Client checks the RP ID against the origin and asks the authenticator for an assertion.
- Authenticator signs the concatenation of its authenticator data and the SHA-256 hash of clientDataJSON with the credential private key, after user presence and, if requested, verification.
- Server verifies the assertion against the stored public key and updates the counter and backup state.
Conditional mediation (the browser offering passkeys in the username field's autofill) is a client UI mode for the same ceremony. It does not change what the server must verify.
Authenticator data and flags
Authenticator data is a compact binary structure that the authenticator signs. Every server library parses it; reviewers should know what each part means.
| Field | Size | Meaning |
|---|---|---|
| rpIdHash | 32 bytes | SHA-256 of the RP ID the authenticator scoped the credential to; must equal the hash of your RP ID |
| flags | 1 byte | Bit 0 UP user present; bit 2 UV user verified; bit 3 BE backup eligible; bit 4 BS backup state; bit 6 AT attested credential data included; bit 7 ED extension data included |
| signCount | 4 bytes | Signature counter; many synced passkeys always report zero |
| attestedCredentialData | Variable (registration) | AAGUID (16 bytes, identifies the authenticator model), credential ID length and ID, COSE-encoded public key |
| extensions | Variable | CBOR map of authenticator extension outputs |
- UV and uvInitialized: Level 3 says the UV flag should not count as an authentication factor until the credential record's uvInitialized is set, and that changing it from false to true should require an additional factor. This stops an attacker enrolling a credential without UV and later claiming verification.
- BE and BS: BE is fixed when the credential is created; BS can change. A BE credential is multi-device (synced); a non-BE credential is single-device, bound to one authenticator. Store both and use them in policy: for example, require single-device credentials for privileged administrators, or prompt users with a non-backed-up credential to add a second one.
- Signature counter: store the latest value. If an authenticator reports a non-zero counter that does not increase, the credential may have been cloned; decide in advance whether to block, step up or just record it. A counter that is always zero carries no signal.
Relying party verification checklist
These are the checks a server or library must perform. Most library bugs and integration mistakes are a missing row.
| Check | Registration | Authentication | What goes wrong if skipped |
|---|---|---|---|
| clientDataJSON type | webauthn.create | webauthn.get | A registration response replayed as a login, or the reverse |
| challenge | Matches the stored, unexpired, unused challenge | Same | Replay of captured responses |
| origin (and topOrigin if crossOrigin) | Exactly one of your expected origins | Same | Loss of phishing resistance if any subdomain or embedding origin is accepted |
| rpIdHash | Equals SHA-256 of your RP ID | Same | Credentials from another RP accepted |
| UP flag | Set | Set | Silent signing without a user gesture |
| UV flag | Set if policy requires | Set if policy requires | Passkey treated as two factors when no PIN or biometric was used |
| Algorithm | One you offered | Matches stored key | Weak or unexpected algorithms |
| Attestation | Verify statement and chain if you requested it | Not applicable | Policy on authenticator models not enforced |
| Credential ID | Not already registered to another user | Belongs to the user (and user handle matches for discoverable) | Account confusion or takeover |
| Signature | Over authData and clientDataJSON hash (self attestation or attestation key) | With stored public key | Forged assertions |
| signCount and BS | Store | Compare and update | Clone detection and backup visibility lost |
Use a maintained, conformance-tested server library rather than hand-parsing CBOR and COSE. OWASP ASVS 5.0 V6.3.3 requires multi-factor authentication and, at level 3, a hardware-based factor; WebAuthn with UV and a single-device credential meets that intent.
Attestation and the FIDO Metadata Service
Attestation lets the authenticator prove what it is. Formats include packed, tpm, android-key, apple, fido-u2f and none. The AAGUID in attested credential data identifies the authenticator model, and the FIDO Metadata Service (MDS) maps models to metadata statements and certification status.
- MDS BLOB: the FIDO Alliance publishes a single signed JSON BLOB of certified authenticators. It is public, must be verified against the GlobalSign R46 root, and FIDO recommends downloading it monthly and caching it. Commercial relying parties need a Metadata Usage Terms licence.
- When to require attestation: workforce access to sensitive systems, regulated environments that mandate certified hardware (for example FIDO certification level or FIPS-validated keys), and when you issue the keys yourself.
- When not to: consumer sign-in. Requiring attestation excludes most synced passkeys, adds privacy concerns and gives little benefit when the threat is phishing rather than counterfeit hardware.
- Enterprise attestation: returns a uniquely identifying attestation for keys provisioned to your organisation, and only when the platform allows it for your RP ID; use it for inventory of issued keys.
| Scenario | Credential type | Attestation | UV |
|---|---|---|---|
| Consumer accounts | Synced passkeys (BE allowed) | none | preferred or required |
| Workforce general access | Synced or device-bound passkeys | none or direct with an allow list | required |
| Privileged administrators | Single-device keys (BE not set) | direct or enterprise, checked against MDS | required |
| Shared or frontline devices | Security keys | direct | required (PIN) |
Level 3 features worth knowing
- Related origin requests: an RP can let a limited set of other origins use its passkeys by publishing a JSON
originslist athttps://<rp-id>/.well-known/webauthnwith content type application/json. Clients must support at least five unique labels, which is the practical limit. Use it for country domains or rebrands, not as a substitute for federation. - prf extension: lets the RP obtain a pseudo-random value derived from the credential and a salt, built on CTAP's hmac-secret. It enables client-side encryption keys tied to a passkey, for example for end-to-end encrypted vaults. Treat the output as key material and never send it to the server unless that is the design.
- largeBlob: stores a small opaque blob with the credential on supporting authenticators, for example certificates.
- credProps: tells the RP whether a discoverable credential was created.
- getClientCapabilities(): lets the RP detect client support, such as related origins, before choosing a flow.
- Legacy appid and appidExclude: for migrating U2F credentials registered under an old AppID.
Passkey portability between providers is a separate FIDO effort; see the FIDO Credential Exchange Passkey Portability Guide.
Threats WebAuthn does and does not address
| Threat | ATT&CK | WebAuthn effect | Residual control needed |
|---|---|---|---|
| Credential phishing and real-time relay | T1557 Adversary-in-the-Middle, T1111 MFA Interception | Prevented when origin and rpIdHash are verified: the phishing origin cannot get a valid assertion | Remove phishable fallback methods |
| Session token theft after login | T1539 Steal Web Session Cookie | Not addressed: WebAuthn protects the login, not the session | Short sessions, token binding where available, infostealer defence |
| Weak recovery and enrolment | Social engineering | Not addressed: an attacker who enrols their own passkey through recovery owns the account | Strong recovery and help desk verification |
| Tampered authentication flow | T1556 Modify Authentication Process | Not addressed if the attacker controls the IdP or server configuration | Protect IdP admin roles and monitor policy changes |
| Cloned authenticator | Not applicable | Detectable only through the counter on authenticators that use one | Device-bound keys for high assurance |
The Twilio breach shows the relay problem: fake SSO pages captured passwords and one-time codes and replayed them in real time, and Twilio moved its staff to FIDO2 security keys afterwards. See the AiTM Phishing Guide.
Implementation and testing
- Choose and pin a library: pick a maintained server library, record its version and track its advisories.
- Fix the RP ID decision early: the RP ID is effectively permanent for issued credentials; choose the registrable domain you will keep, and plan related origins if you have several.
- Define policy per population: UV, BE allowance and attestation per user group, as in the table above, written down so reviewers can test against it.
- Negative tests: in a test environment, submit responses with a wrong origin, wrong RP ID hash, reused challenge, UP cleared, UV cleared when required, and a registration response at the login endpoint. Every one must fail.
- Pilot and monitor: roll out to a pilot group with the old method still available, monitor ceremony errors by authenticator AAGUID and platform, then remove phishable fallbacks group by group. Rolling back means re-enabling the fallback, so keep it configured but disabled rather than deleted during the pilot.
What to log and measure: registration and authentication outcomes with failure reason (origin mismatch, challenge expired, signature invalid); AAGUID and BE/BS state per credential; counter regressions; share of sign-ins using WebAuthn with UV; share of accounts with at least two registered credentials; recovery events that enrol a new credential, which should alert for privileged users.
Common failure patterns
- Loose origin matching: accepting any subdomain or using a suffix match on origin reopens phishing through a compromised subdomain.
- Challenge handling shortcuts: static challenges in testing that reach production, challenges not deleted after use, or challenges generated by client-side code.
- Ignoring UV: treating any passkey sign-in as multi-factor even when the authenticator did not verify the user.
- Phishable fallbacks left on: SMS or OTP still available "just in case" means attackers simply choose them, as relay campaigns such as the one in the Twilio breach do.
- Recovery that bypasses everything: a help desk that resets to email OTP undoes the strongest authenticator. See the Account Recovery and Help Desk Security Guide.
- Personal data in the user handle: using an email address as the user handle leaks it to authenticators and breaks the Level 3 rule.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev. 5 | IA-2(1), IA-2(2), IA-2(8), IA-5(2), IA-7 | MFA for privileged and non-privileged accounts, replay resistance, public key authentication, cryptographic module authentication |
| NIST CSF 2.0 | PR.AA-03 | Users, services and hardware are authenticated |
| OWASP ASVS 5.0 | V6.3.3 | MFA required; hardware-based factor at level 3 |
| MITRE ATT&CK | T1557, T1111, T1539, T1556 | Threats mitigated and not mitigated, as above |
See the Phishing-Resistant MFA Guide for how WebAuthn compares with other phishing-resistant methods.
Practitioner checklist
- Use a maintained, conformance-tested WebAuthn server library and track its advisories.
- Generate random, single-use, server-stored challenges with a short expiry.
- Verify type, challenge, exact origin, rpIdHash, UP and UV on every ceremony.
- Store and act on BE, BS, uvInitialized and the signature counter.
- Decide attestation per population; for privileged users verify against the FIDO Metadata Service BLOB.
- Use opaque user handles with no personal data.
- Run negative tests for wrong origin, reused challenge and missing flags before go-live.
- Remove phishable fallback methods once enrolment coverage allows, and harden recovery.
- Alert when a new credential is registered for a privileged account.
Standards and references
- W3C: Web Authentication, An API for Accessing Public Key Credentials, Level 3
- FIDO Alliance: Specifications (CTAP, Metadata Service, Metadata Statements)
- FIDO Alliance: Metadata Service
- passkeys.dev: Related Origin Requests
- OWASP: Application Security Verification Standard 5.0
- NIST: SP 800-53 Rev. 5
Related NHI Mgmt Group resources: Passwordless and Passkeys Guide · Passkey Rollout Guide · Phishing-Resistant MFA Guide · Hardware Security Keys Guide