Skip to content

fix(passkeys): always require user verification in WebAuthn ceremonies#20725

Open
vpomerleau wants to merge 1 commit into
mainfrom
fix/passkey-require-user-verification
Open

fix(passkeys): always require user verification in WebAuthn ceremonies#20725
vpomerleau wants to merge 1 commit into
mainfrom
fix/passkey-require-user-verification

Conversation

@vpomerleau

@vpomerleau vpomerleau commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Because

  • Passkey sign-in mints an AAL2 session token and bypasses TOTP, so user
    verification must always occur. The server's @simplewebauthn/server verify
    calls omitted requireUserVerification (which defaults to false), so a
    WebAuthn response with the user-verified flag cleared was accepted — granting
    an AAL2 session to a present-but-unverified user.
  • User verification was also a configurable knob that could be relaxed to
    'preferred'/'discouraged', weakening the guarantee. This must be fixed
    before the Phase 1 production rollout.

This pull request

  • Hardcodes requireUserVerification: true on both
    verifyWebauthnRegistrationResponse and verifyWebauthnAuthenticationResponse,
    and userVerification: 'required' on both option-generation functions in
    libs/accounts/passkey/src/lib/webauthn-adapter.ts.
  • Removes the userVerification config knob entirely — from PasskeyConfig,
    RawPasskeyConfig, and the convict PASSKEYS__USER_VERIFICATION setting in
    packages/fxa-auth-server/config/index.ts — so user verification can no
    longer be relaxed.
  • Narrows the route response schemas in
    packages/fxa-auth-server/lib/routes/passkeys.ts to valid('required'),
    dropping the now-unreachable 'preferred'/'discouraged' values.
  • Adds an optional userVerified flag to the test VirtualAuthenticator and
    regression tests asserting a UV-unset response is rejected for both ceremonies.

Issue that this pull request solves

Closes: FXA-13941

Checklist

Put an x in the boxes that apply

  • My commit is GPG signed.
  • If applicable, I have modified or added tests which pass locally.
  • I have added necessary documentation (if appropriate).
  • I have verified that my changes render correctly in RTL (if appropriate).
  • I have manually reviewed all AI generated code.

How to review (Optional)

  • Key files/areas to focus on: webauthn-adapter.ts (the enforcement — both requireUserVerification: true and the 'required' options); the knob removal across passkey.config.ts, passkey.provider.ts, and config/index.ts.
  • Suggested review order: adapter → config removal → virtual-authenticator.ts UV-flag bit math → the new negative tests.
  • Risky or complex parts: the authenticator-data flags byte in virtual-authenticator.ts (UV bit clearing) underpins the regression tests.

Screenshots (Optional)

Please attach the screenshots of the changes made in case of change in user interface.

Other information (Optional)

  • webservices-infra should drop the PASSKEYS__USER_VERIFICATION env var if it is set in any stage/prod config. With the convict key removed it is now inert (convict ignores an env var with no matching schema key, so there is no runtime break), but it should be cleaned up so it doesn't linger as a dead variable.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR strengthens passkey/WebAuthn security by making user verification (UV) a non-configurable invariant and enforcing it server-side during both registration and authentication ceremonies, preventing issuance of an AAL2 session when the authenticator did not perform UV.

Changes:

  • Hardcode userVerification: 'required' when generating WebAuthn options and enforce requireUserVerification: true when verifying responses.
  • Remove the userVerification configuration knob (including the convict env var) so it cannot be relaxed.
  • Update route schemas and extend the VirtualAuthenticator/tests to cover UV-unset negative cases.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/fxa-auth-server/lib/routes/passkeys.ts Narrows response schema values for userVerification to 'required'.
packages/fxa-auth-server/config/index.ts Removes PASSKEYS__USER_VERIFICATION convict config entry.
libs/accounts/passkey/src/lib/webauthn-adapter.ts Forces UV-required option generation and enforces UV during verification.
libs/accounts/passkey/src/lib/webauthn-adapter.spec.ts Adds regression tests ensuring UV-unset responses are rejected; updates existing expectations.
libs/accounts/passkey/src/lib/virtual-authenticator.ts Adds a test hook to clear the UV flag in authenticator-data for negative testing.
libs/accounts/passkey/src/lib/passkey.service.spec.ts Removes userVerification from PasskeyConfig test setup.
libs/accounts/passkey/src/lib/passkey.provider.ts Removes userVerification from raw config type/imports.
libs/accounts/passkey/src/lib/passkey.provider.spec.ts Updates raw config fixture expectations to match knob removal.
libs/accounts/passkey/src/lib/passkey.config.ts Removes userVerification from PasskeyConfig and its validation/docs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/fxa-auth-server/lib/routes/passkeys.ts Outdated
Comment thread packages/fxa-auth-server/lib/routes/passkeys.ts Outdated
Comment thread libs/accounts/passkey/src/lib/webauthn-adapter.spec.ts Outdated
Comment thread libs/accounts/passkey/src/lib/webauthn-adapter.spec.ts
Comment thread libs/accounts/passkey/src/lib/webauthn-adapter.spec.ts
@vpomerleau vpomerleau force-pushed the fix/passkey-require-user-verification branch from 5d9f6d8 to e49b0aa Compare June 10, 2026 16:13
Because:
 - Passkey sign-in mints an AAL2 session token and bypasses TOTP, so user
   verification must always occur. The simplewebauthn verify calls omitted
   requireUserVerification (defaults to false), so a UV-unset response was
   accepted; UV was also a config knob that could be relaxed to 'preferred'.

This commit:
 - Hardcodes requireUserVerification: true on both the registration and
   authentication verify calls, and userVerification: 'required' on both
   option-generation calls.
 - Removes the userVerification config knob (PasskeyConfig, RawPasskeyConfig,
   and convict PASSKEYS__USER_VERIFICATION) so UV can no longer be relaxed.
 - Adds an optional userVerified flag to the test VirtualAuthenticator and
   regression tests asserting a UV-unset response is rejected.

Closes: FXA-13941
@vpomerleau vpomerleau force-pushed the fix/passkey-require-user-verification branch from e49b0aa to 8eb5055 Compare June 10, 2026 16:42
@vpomerleau vpomerleau marked this pull request as ready for review June 10, 2026 16:43
@vpomerleau vpomerleau requested a review from a team as a code owner June 10, 2026 16:43
.string()
.valid('discouraged', 'preferred', 'required')
.optional(),
userVerification: isA.string().valid('required').required(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏽

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants