WebAuthn Level 3 support

This page maps the app against the relying-party (RP) requirements of Web Authentication Level 3 (W3C Recommendation, 25 August 2026). It was checked on 2026-09-24 against the app’s source and the pinned server library, py_webauthn 2.8.0.

“py_webauthn” in the tables means the check runs inside the library’s verify_registration_response / verify_authentication_response, which the app calls from passkeys/engine.py. Everything else is app code.

Fully supported

Registering a new credential (§7.1)

Spec step Where Notes
Parse clientDataJSON (UTF-8, JSON) py_webauthn  
C.type is webauthn.create py_webauthn  
C.challenge matches the issued challenge py_webauthn Challenge is single-use server state, consumed before verification (state.py).
C.origin is expected py_webauthn + app Exact match against the site’s configured origin list (policy.py); the request host must also be in that list (ceremony.py).
C.crossOrigin / C.topOrigin app, engine.py _reject_cross_origin The RP expects no cross-origin iframes, so either member rejects the ceremony. py_webauthn does not check these.
rpIdHash is SHA-256 of the RP ID py_webauthn  
UP flag set unless mediation: "conditional" py_webauthn, flag set by api/registration.py UP is waived only for the server-recorded conditional-create flow, never on client input.
UV flag when the RP requires it app Registration does not require UV; the UV bit is recorded as uvInitialized and gates later use (see below).
BS set only if BE set py_webauthn Its own exception branch in engine.py, so it fails as a clean 401.
Credential alg is one of the offered pubKeyCredParams py_webauthn + app Offered: EdDSA, ES256, RS256. The app re-reads alg from the COSE key, not from the client-reported value.
Attestation format and statement verification py_webauthn The app requests attestation: "none"; see Not supported for trust anchors.
Credential ID ≤ 1023 bytes app, engine.py  
Credential ID not already registered for any user app, api/registration.py Global unique index on the credential ID hash; a duplicate fails closed.
Store the credential record app, api/registration.py Stores id, public key, sign count, uvInitialized, transports, BE, BS, attestation object, client data, RP ID, and a user-editable label.
Process extension outputs app, engine.py credProps.rk is read from the raw response and stored as discoverable Yes / No / Unknown. Other outputs are ignored.
Store under the user in pkOptions.user app, api/registration.py Ceremony state is bound to the user and session that began it.

Verifying an authentication assertion (§7.2)

Spec step Where Notes
Credential is in allowCredentials when that list is non-empty app, passkey.py (second factor), confirm.py (action confirmation) Passwordless login uses an empty list (discoverable credentials).
Identify the user: credential belongs to the account; userHandle matches when present app, passkey.py, confirm.py Passwordless login requires userHandle and resolves the account from it.
Parse clientDataJSON py_webauthn  
C.type is webauthn.get py_webauthn  
C.challenge matches py_webauthn Single-use, consumed before verification.
C.origin is expected py_webauthn + app As for registration.
C.crossOrigin / C.topOrigin app, engine.py Rejected, as for registration.
rpIdHash py_webauthn  
UP flag set py_webauthn  
UV flag when required app Required for passwordless login and action confirmation (the UV bit must be set). A false uvInitialized routes passwordless login to the password step-up, and confirmation to a password- or reauth-seeded sudo window. Not required for passkey as a second factor, where the password is the other factor.
BS set only if BE set py_webauthn  
Stored BE vs asserted BE app, engine.py + policy.py A change in either direction rejects the assertion.
Signature over authData ‖ SHA-256(clientDataJSON) py_webauthn  
Signature counter app, engine.py + ceremony.py An equal non-zero counter (replay) always rejects. A regression flags the credential and notifies the owner; a setting turns it into a hard failure. The stored value only moves up.
Update backupState app, ceremony.py Refreshed on every successful assertion.
Update uvInitialized only with an additional factor app, passkey.py, confirm.py The false→true flip needs password knowledge proven in the session (a step-up password, the second-factor password, or a password- or reauth-seeded sudo window).
Defer state updates until the RP’s extra checks pass app, passkey.py Counter and flag updates run after the password-version and account checks, before the session is created.

Client features

Feature Where Notes
Conditional mediation (passkey autofill) passkey_login.bundle.js, passkey_headless.bundle.js With autocomplete="username webauthn".
Conditional create (automatic passkey upgrade) passkey_desk.bundle.js, api/registration.py Offered only after a password sign-in.
signalUnknownCredential passkey_login.bundle.js Sent when the server reports an unknown credential.
signalAllAcceptedCredentials, signalCurrentUserDetails passkey_manage_common.bundle.js After adding, removing or listing passkeys.
getClientCapabilities passkey_common.bundle.js Layered detection with fallback to isConditionalMediationAvailable / isUserVerifyingPlatformAuthenticatorAvailable.
parseCreationOptionsFromJSON / parseRequestOptionsFromJSON / toJSON passkey_common.bundle.js Manual fallbacks for browsers without them.
credProps extension client bundles + engine.py Requested on every registration.
Hybrid (phone QR) sign-in browser authenticatorAttachment is never restricted, so cross-device sign-in stays available.

Not supported

Feature Why
Related Origin Requests (/.well-known/webauthn) Not implemented. Origins must be the RP ID or its subdomains; one site across several unrelated domains is not supported yet.
Attestation trust anchors / FIDO Metadata Service By design. The app requests attestation: "none" and does not evaluate certificate chains. The AAGUID is used only to show a provider name, never for policy.
hints Not sent. Browsers fall back to their default authenticator choice.
Identifier-first login (username, then a non-empty allowCredentials) Not implemented. Passwordless login uses discoverable credentials only; passkey as a second factor does use allowCredentials.
Extensions other than credProps (prf, largeBlob, appid, …) Not requested; unsolicited outputs are ignored.
Cross-origin iframe ceremonies Rejected by design (crossOrigin / topOrigin), so a framing page cannot run a ceremony for the site.

See Security for the wider security model and its known limitations.