Skip to content

fix(passkeys): validate base64url WebAuthn fields at the route boundary#20658

Open
vpomerleau wants to merge 1 commit into
mainfrom
FXA-13808
Open

fix(passkeys): validate base64url WebAuthn fields at the route boundary#20658
vpomerleau wants to merge 1 commit into
mainfrom
FXA-13808

Conversation

@vpomerleau
Copy link
Copy Markdown
Contributor

@vpomerleau vpomerleau commented May 28, 2026

Because

  • An OAST probe (Sentry FXA-AUTH-2T4) hit POST /v1/passkey/authentication/finish with a non-base64url response.id. Validation was deferred to base64urlToBuffer at the DB boundary, which throws an Error and surfaces as a 500 — polluting Sentry with what should be a 400.
  • Same shape on three sibling passkey routes (registration/finish, DELETE, PATCH); future probes there would reproduce the pattern.

This pull request

  • Adds local Joi factories (base64urlString(maxLen), base64urlCredentialId(), base64urlChallenge()) in packages/fxa-auth-server/lib/routes/passkeys.ts. Credential-ID .max(1364) matches the WebAuthn-L2 ceiling and the passkeys.credentialId VARBINARY(1023) column.
  • Applies them to response.id, rawId, challenge, and the inner response.response.* base64url fields (clientDataJSON, attestationObject, authenticatorData, signature, userHandle, publicKey) on registration/finish + authentication/finish, and to params.credentialId on DELETE/PATCH.
  • Tightens challenge on registration/finish to match authentication/finish.
  • Adds Joi-direct schema tests in packages/fxa-auth-server/lib/routes/passkeys.spec.ts covering positive, alphabet/length rejection, and missing-required-field cases. Assertions pin to error.details[].path + error.details[].type (Joi-version-resilient).

Issue that this pull request solves

Closes: FXA-13808

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: packages/fxa-auth-server/lib/routes/passkeys.ts — factories at top + four route schemas.
  • Suggested order: factories → route schemas → tests.
  • Risky bit: strict base64url is narrower than SimpleWebAuthn's lenient internal decoder. Major browsers and @simplewebauthn/browser emit spec-strict, and the prior base64urlToBuffer already enforced strict on response.id with no real-client 500s in Sentry — so empirical risk is very low.

Screenshots (Optional)

N/A — server-only change.

Other information (Optional)

  • Scope expansion vs ticket: the original "Affected Surface" listed only response.id / challenge / params.credentialId. During /fxa-review, the same defense-in-depth was extended to the inner response.response.* WebAuthn fields, which SimpleWebAuthn decodes internally and could throw to 500 the same way. Folded into this PR rather than a follow-up.
  • Out of scope (deferred): promoting the factories to lib/routes/validators.js for cross-route reuse; rate-limit tuning for passkeyAuthFinish (separate, lives in webservices-infra).

Because: Malformed base64url values in passkey route payloads were caught
only at the DB/SimpleWebAuthn boundary, throwing Errors that Hapi surfaced
as 500s and pollute Sentry — e.g. FXA-AUTH-2T4, an OAST callback probe on
POST /v1/passkey/authentication/finish.

This commit:
- adds local base64url Joi factories (BASE64URL_PATTERN, base64urlString,
  base64urlCredentialId, base64urlChallenge) in passkeys.ts
- applies them to response.id/rawId/challenge and inner-response base64url
  fields (clientDataJSON, attestationObject, authenticatorData, signature,
  userHandle, publicKey) on registration/finish and authentication/finish;
  and to params.credentialId on DELETE/PATCH
- tightens the challenge validator on registration/finish to match
  authentication/finish
- adds Joi-direct schema-validation tests asserting on error.details.type

Closes #FXA-13808
@vpomerleau vpomerleau marked this pull request as ready for review May 28, 2026 19:29
@vpomerleau vpomerleau requested a review from a team as a code owner May 28, 2026 19:29
Copilot AI review requested due to automatic review settings May 28, 2026 19:29
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

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