Skip to content

fix(passkeys): pass challenge as Buffer to simplewebauthn to fix lookup#20362

Merged
dschom merged 1 commit intomainfrom
FXA-13343.2
Apr 15, 2026
Merged

fix(passkeys): pass challenge as Buffer to simplewebauthn to fix lookup#20362
dschom merged 1 commit intomainfrom
FXA-13343.2

Conversation

@dschom
Copy link
Copy Markdown
Contributor

@dschom dschom commented Apr 10, 2026

Because

  • simplewebauthn v13 treats string challenges as UTF-8 text and re-encodes them to base64url, producing a different value than what was stored in Redis. This caused challenge lookup to fail on registration/authentication finish.
  • Calls to the sipmlewebauthn lib were being unnecessarily mocked, so we didn't catch this.
  • We had a few spots where we were not buffer encoding correctly. Strings are hex strings, and UID are 16 bit length buffers.

This pull request

  • Passes challenge as Buffer.from(challenge, 'base64url') to generateRegistrationOptions and generateAuthenticationOptions so simplewebauthn base64url-encodes the raw bytes (roundtrip-safe).
  • Adds webauthn-adapter.in.spec.ts with a virtual authenticator that exercises real simplewebauthn verifyRegistrationResponse (no mocks).
  • Adds happy-path integration test for registration start → finish.
  • Fixes uid buffer encodings

Issue that this pull request solves

Closes: FXA-13343

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: Take a good look at the web authn tests now. These were updated by claude.
  • Suggested review order: Webauthn then other parts
  • Risky or complex parts: The virutal authenticator was all gen ai code.

Screenshots (Optional)

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

Other information (Optional)

At first it appeared that challenges output by libGenerateAuthenticationOptions were always changing from the original challenge that is provided to the library call. I assumed that this was by design, however, after adding more tests that actually exercised the library call and doing some debugging it appears the core issue was just the encoding on the challenge. (Which is what Claude thought.)

There was one red herring in previous tests worth noting. When using an arbitrary challenge like test-challenge in tests, failures would occur since it doesn't encode cleanly, e.g. Buffer.from('test-challenge', 'base64url').toString('base64url'); -> 'test-challengQ'. Using a actual challenge value like const challenge = randomBytes(32).toString('base64url'); won't have this problem since the challenge is already base64 encoded.

@dschom dschom changed the title fix(passkeys): pass challenge as Buffer to simplewebauthn to fix lookup WIP - fix(passkeys): pass challenge as Buffer to simplewebauthn to fix lookup Apr 10, 2026
@dschom dschom force-pushed the FXA-13343.2 branch 2 times, most recently from 1dca6f7 to d99d74d Compare April 15, 2026 14:54
@dschom dschom marked this pull request as ready for review April 15, 2026 15:18
@dschom dschom requested a review from a team as a code owner April 15, 2026 15:18
@dschom dschom requested a review from vpomerleau April 15, 2026 15:20
@dschom dschom force-pushed the FXA-13343.2 branch 2 times, most recently from f1cfa5c to 1e1e1bb Compare April 15, 2026 17:23
export * from './lib/passkey.provider';
export * from './lib/passkey.challenge.manager';
export * from './lib/webauthn-adapter';
export * from './lib/virtual-authenticator';
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.

This is test scaffolding — should it be included in the public barrel? Noting a similar question about passkey.repository and webauthn-adapter, perhaps a quick follow-up?

Copy link
Copy Markdown
Contributor

@vpomerleau vpomerleau left a comment

Choose a reason for hiding this comment

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

Thank you for spotting and resolving the challenge failure. The addition of new tests with virtual authenticator (instead of mocks) is much appreciated. I could see potential for expanding tests to cover more options (like backupState, prfEnabled, signCount regression), but major paths all look covered. No blockers other than ensuring that tests pass, most comments are questions/non-blocking.

Comment thread packages/fxa-auth-server/test/remote/passkeys.in.spec.ts Outdated
Comment thread libs/accounts/passkey/src/lib/webauthn-adapter.spec.ts
const options = await generateWebauthnRegistrationOptions(
testConfig({
residentKey: 'required',
userVerification: 'discouraged',
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.

Is there a reason to use discouraged and not required here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There is not. This gen-ai, but it seemed okay to me. I can switch to required if you think that's more typical.

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.

We're planning to always require user verification

* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
* Integration tests for the webauthn-adapter that exercise the real
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.

Do we need a ticket to add similar tests for authentication?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think it'd be helpful. That would ensure wiring it all up goes smoothly.

Comment thread libs/accounts/passkey/src/lib/webauthn-adapter.in.spec.ts Outdated
@dschom dschom changed the title WIP - fix(passkeys): pass challenge as Buffer to simplewebauthn to fix lookup fix(passkeys): pass challenge as Buffer to simplewebauthn to fix lookup Apr 15, 2026
Because:
- simplewebauthn v13 treats string challenges as UTF-8 text and
  re-encodes them to base64url, producing a different value than what
  was stored in Redis. This caused challenge lookup to fail on
  registration/authentication finish.
- There were some issues converting UID to a buffer. We must specify 'hex' when converting.
- The tests mocked simplewebauth lib, but we want to excercise this code to validate our test cases actually work.

This commit:
- Passes challenge as Buffer.from(challenge, 'base64url') to
  generateRegistrationOptions and generateAuthenticationOptions so
  simplewebauthn base64url-encodes the raw bytes (roundtrip-safe).
- Fixes PasskeyService constructor args in the integration test
  (was missing PasskeyConfig parameter).
- Adds webauthn-adapter.in.spec.ts with a virtual authenticator that
  exercises real simplewebauthn verifyRegistrationResponse (no mocks).
- Adds happy-path integration test for registration start → finish.
@dschom dschom merged commit 2584a57 into main Apr 15, 2026
21 checks passed
@dschom dschom deleted the FXA-13343.2 branch April 15, 2026 23:25
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.

2 participants