Why is WebAuthn 2FA hardcoded to UserVerificationPolicy::Discouraged for both registration and login?
#7556
Unanswered
kuchida1981
asked this question in
Q&A
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Environment
Context
generate_webauthn_challenge(registration) andgenerate_webauthn_login(login) both explicitly forceUserVerificationPolicy::Discouraged_DO_NOT_USEon every WebAuthn 2FA ceremony, regardless of what the authenticator is capable of:I traced this back to #1840 (2021), where a wave of different security keys (YubiKey, NitroKey, SoloKey) started failing login with
UserNotVerifiedafterget_require_uv_consistencydefaulted totruein the webauthn-rs 0.3.x config at the time. @dani-garcia's own reply there frames disabling it (false) as "the more compatible option," and that's what shipped in ffdcafa. The webauthn-rs 0.5.x migration (#5934) carried the samediscouragedchoice forward without revisiting it.What we ran into
A Google Titan Security Key registers fine as FIDO2 WebAuthn 2FA, but every login attempt afterwards hangs at the touch prompt and eventually fails with
NotAllowedError.Root cause, confirmed by a controlled re-registration test: registering the same physical key via Chrome (which honors the
discouragedhint literally and never performs UV - no PIN prompt) produces a credential that can never complete login. Registering it via Firefox instead (which prompts for the PIN despite the samediscouragedhint) produces a credential that works from both browsers. The two DB records differ specifically inuser_verified(falsevstrue) and credential ID length (160 vs 288 bytes) - this key's firmware apparently can't correctly handle a laterGetAssertionagainst a credential ID that was wrapped without UV having been performed at registration.The fix we tested
Changed both registration and login to request
UserVerificationPolicy::Preferredinstead: kuchida1981/vaultwarden#1.registration_policywas now stored aspreferred, which activates an enforcement branch inwebauthn-rs-core'sverify_credential_internal(cred.user_verified && !data.authenticator_data.user_verified => UserNotVerified) - but since login was still forcingdiscouraged, neither browser was ever asked to perform UV during the login ceremony, so every login failed regardless of browser. Switching login toPreferredtoo resolved this: whichever browser is used now actually prompts for the PIN, and login succeeds.registration_policy: discouragedin the DB, which structurally bypasses that enforcement branch entirely - so existing keys shouldn't be affected either way.Question
Given #1840's history, was
Discouragedchosen deliberately as a durable policy (e.g. because some authenticators are known to misbehave underPreferred, and the "more compatible option" framing was meant to stick long-term), or was it more of a quick, broad fix for the 2021 wave of failures that just never got revisited since? I want to understand whether switching the default toPreferredrisks reintroducing that same class of failure for other authenticators, before proposing it as a real PR - happy to open one if there's interest and no known landmine here.All reactions