feat(passkey): Add verification proof to the challenge manager - #21114
feat(passkey): Add verification proof to the challenge manager#21114nshirley wants to merge 1 commit into
Conversation
| return false; | ||
| } | ||
|
|
||
| if (stored.credentialId !== credentialId) { |
There was a problem hiding this comment.
Should be REALLY unlikely to happen, but defense-in-depth!
30b6aca to
fdcf48f
Compare
| // Matches the config default (passkeys.challengeTimeout = 300,000 ms) | ||
| const CHALLENGE_TIMEOUT_MS = 1000 * 60 * 5; | ||
|
|
||
| // Different from CHALLENGE_TIEMOUT_MS to allow tests to deterministically default behavior of proofs |
There was a problem hiding this comment.
| // Different from CHALLENGE_TIEMOUT_MS to allow tests to deterministically default behavior of proofs | |
| // Different from CHALLENGE_TIMEOUT_MS so tests are able to deterministically verify which timeout is used |
|
|
||
| /** | ||
| * Length in base64url characters of a challenge or proof token, which are 32 | ||
| * random bytes. Used to validate a proof on the wire before hitting Redis. |
There was a problem hiding this comment.
| * random bytes. Used to validate a proof on the wire before hitting Redis. | |
| * random bytes. Used for Joi validation of the proof before hitting Redis. |
Because: * A wrap write needs proof that a user-verified assertion just completed for a specific credential, which a session token alone cannot attest. * A proof is the same primitive as a challenge — a random value minted, then consumed once — so a second Redis module would be duplication. This commit: * Adds a 'proof' challenge type, separately typed so an upgrade challenge handed out before any assertion cannot be spent as a proof. * Adds generateProofChallenge and consumeProofChallenge, credential-scoped. * Takes the proof TTL from PASSKEYS__VERIFICATION_PROOF_TIMEOUT, which spans the password entry a ceremony challenge does not. * Adds errno 236, PASSKEY_VERIFICATION_PROOF_INVALID, and its AppError factory.
vpomerleau
left a comment
There was a problem hiding this comment.
This looks straightforward, just some naming nits. 🎉
| * vice versa. UID is required when generating an upgrade challenge. | ||
| * | ||
| * - 'proof': Not a ceremony challenge. Minted *after* an assertion verifies and | ||
| * consumed once to authorise key creation for that credential. Carries no key |
There was a problem hiding this comment.
Nit: Is it too specific to note this is specifically for key/wrap creation? Conceivably we could use this proof to authorize other actions in the future.
There was a problem hiding this comment.
It's a good idea, but I don't think we would want to use this for that at this time. The challenges (create, proof, any of them) don't carry any additional authorization material like scopes, so anything that mints a proof could then use that same proof if we accept it on multiple endpoints.
That said, I think we could explore allowing passkeys as an option to mint the existing MFA JWT tokens. But that would likely need a lot more thought, UX, design, etc since we'd have to handle opt-in, fall backs, etc
| * - 'proof': Not a ceremony challenge. Minted *after* an assertion verifies and | ||
| * consumed once to authorise key creation for that credential. Carries no key | ||
| * material; it attests only that the assertion happened. Typed separately from | ||
| * 'upgrade' because an upgrade challenge is issued before any assertion — were |
There was a problem hiding this comment.
Nit: We currently are not using the upgrade challenge, and I wonder if we should rename more generically to authorization challenge? This would conceivably be the challenge we need whenever an already authenticated user uses the passkey to authorize account changes (such as storing a new key wrap or as alternative to the email code MFA).
There was a problem hiding this comment.
Pull request overview
Extends the passkey challenge manager with credential-scoped, single-use verification proofs for subsequent key-wrap creation.
Changes:
- Adds proof generation and consumption with independent TTL configuration.
- Adds proof validation errors and errno 236.
- Adds unit and integration coverage for proof scope, expiry, and single use.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
packages/fxa-auth-server/config/index.ts |
Adds proof timeout configuration. |
libs/accounts/passkey/src/lib/webauthn-adapter.spec.ts |
Updates test configuration. |
libs/accounts/passkey/src/lib/passkey.service.spec.ts |
Updates service test configuration. |
libs/accounts/passkey/src/lib/passkey.provider.ts |
Extends raw passkey configuration. |
libs/accounts/passkey/src/lib/passkey.provider.spec.ts |
Updates provider fixtures. |
libs/accounts/passkey/src/lib/passkey.manager.spec.ts |
Updates manager fixtures. |
libs/accounts/passkey/src/lib/passkey.manager.in.spec.ts |
Updates integration fixtures. |
libs/accounts/passkey/src/lib/passkey.config.ts |
Models the proof timeout. |
libs/accounts/passkey/src/lib/passkey.challenge.manager.ts |
Implements credential-scoped proofs. |
libs/accounts/passkey/src/lib/passkey.challenge.manager.spec.ts |
Tests proof storage and TTL selection. |
libs/accounts/passkey/src/lib/passkey.challenge.manager.in.spec.ts |
Tests proof isolation and single use. |
libs/accounts/errors/src/index.spec.ts |
Tests the new AppError factory. |
libs/accounts/errors/src/constants.ts |
Adds proof-invalid errno 236. |
libs/accounts/errors/src/app-error.ts |
Adds the proof-invalid AppError factory. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| @IsNumber() | ||
| public verificationProofTimeout!: number; |
|
After further conversation, we've decide to not implement this at this time. If we need it down the road we can always re-open or reference this pr |
Because:
This commit:
Closes: Part of FXA-13142 (sister pr is the bulk of that work)
Checklist
Put an
xin the boxes that applyHow to review (Optional)
Screenshots (Optional)
Please attach the screenshots of the changes made in case of change in user interface.
Other information (Optional)
Any other information that is important to this pull request.