Requests with `uv = required` and `uv = preferred` are sent to CTAP2 devices with unconfigured user verification

Open
#373 0 comments 0 reactions 1 assignee View on GitHub

@micolous is already working on this.

Since Aug 20, 2026.

Assessment

This issue has not been assessed yet.

Description

bug

Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=2070758

In #372, I added options for changing the user verification requirement in the ctap2.rs example, and made it validate the UV bit of the response. While testing #370, I discovered some other issues with the authenticator selection algorithm.

According to CTAP 2.3 §6.1.1 (Platform actions for MakeCredential) and §6.2.1 (Platform Actions for GetAssertion):

  • if an authenticator is not protected by some form of user verification, and user verification is required, the platform should cancel the action and perform some setup process (ie: #223)

  • if the authenticator is protected by some form of user verification or the RP prefers enforcing user verification (uv = preferred | required), it should then it should plan to use UV. If UV is unconfigured (ie: RP prefers enforcing user verification), it should perform some setup process.

According to WebAuthn 3 §5.8.6 (User Verification Requirement) (emphasis added):

CTAP 2.3 §6.1.1 and §6.2.1 suggests we should take the same interpretation of "preferred" as WebAuthn implementations take for §5.4.6 Resident Key Requirement (if an authenticator supports resident keys, they are used). We can then turn this into a truth table:

RP's UV requirement Authenticator supports UV UV is configured Expected outcome
required yes yes use UV
required yes no rejected
required no (CTAP1) - rejected
preferred yes yes use UV
preferred yes no rejected, because UV is "possible"
preferred no (CTAP1) - UV skipped
discouraged yes yes UV skipped if possible[^0]
discouraged yes no UV skipped if possible[^1]
discouraged no (CTAP1) - UV skipped

[^0]: Skipped when alwaysUv = false | unset (CTAP 2.3 §6.1.2 Step 6) and (makeCredUvNotRqd = true && rk = true) || makeCredUvNotRqd = false | unset (Steps 7 and 8)
[^1]: Skipped when alwaysUv = false | unset (CTAP 2.3 §6.1.2 Step 6. Steps 7 and 8 don't apply)

The actual behaviour with a non-resident credential, using USB authenticators that have a PIN configured, that support PIN but aren't configured, and don't support UV at all (CTAP1):

RP's UV requirement Authenticator supports PIN PIN is set Firefox 153 [^4] Chromium 151 webauthn-authenticator-rs 0.6 [^5] Windows 11 25H2 [^6] Safari 26.6.2 on macOS 26.6.2
required yes yes use PIN use PIN use PIN use PIN use PIN
required yes no PIN skipped Prompts to set PIN rejected [^3] Prompts to set PIN Prompts to set PIN
required no (CTAP1) - fails (#371) "can't be used" not supported "can't be used" device ignored
preferred yes yes use PIN use PIN use PIN use PIN use PIN
preferred yes no PIN skipped PIN skipped rejected [^3] Prompts to set PIN PIN skipped
preferred no (CTAP1) - PIN skipped PIN skipped not supported PIN skipped PIN skipped

The discouraged behaviour is correct for all tested implementations.

Firefox/authenticator_rs incorrect behaviour for required then relies on the RP actually checking the UV bit in the response, and some RPs don't [^2]. While this isn't a security issue in Firefox, it is non-spec-compliant behaviour.

[^2]: Not checking the UV bit is absolutely a security issue for the RP, because otherwise an attacker could steal an authenticator, modify the RP's request to set uv = discouraged, and bypass UV. If the RP checks the bit, then this attack would fail.
[^3]: webauthn-authenticator-rs 0.6 doesn't allow a user to configure PIN/UV during a registration or authentication ceremony (ie: doesn't implement "the platform recovers in some fashion out of scope for these actions" per CTAP 2.3 §6.1.1)
[^4]: Using authenticator-rs as a backend, eg: on Linux
[^5]: Using its native ctap2 backend with the usb transport.
[^6]: Tested via webauthn-authenticator-rs 0.6 with the win10 backend, to minimise browser-level interference.

Dominant language
Rust
Stars
316
Forks
82
Avg merge
4d 20h
Merged PRs (30d)
1

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from mozilla/authenticator-rs

All issues in mozilla/authenticator-rs

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.