feat(profile-sync): add MFA step-up sessions - #10267
Open
mathieuartu wants to merge 2 commits into
Open
mathieuartu wants to merge 2 commits into
mathieuartu wants to merge 2 commits into
Conversation
mathieuartu
added this pull request to stack #10268
September 16, 2026 13:42
This was referenced Sep 16, 2026
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
from
September 16, 2026 14:08
58aa78b to
796a725
Compare
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
from
September 16, 2026 20:13
5fe0fb9 to
b6feacb
Compare
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
from
September 16, 2026 20:31
b6feacb to
2c4687d
Compare
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
4 times, most recently
from
September 16, 2026 21:03
7826fcb to
e84159d
Compare
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
from
September 17, 2026 06:48
e84159d to
ab66eab
Compare
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
from
September 17, 2026 06:53
ab66eab to
15ab75a
Compare
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
2 times, most recently
from
September 17, 2026 07:46
9c60aec to
26775e8
Compare
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
from
September 17, 2026 08:12
26775e8 to
5478a59
Compare
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
2 times, most recently
from
September 17, 2026 13:41
b53422a to
d418aab
Compare
pull Bot
pushed a commit
to Reality2byte/core
that referenced
this pull request
Sep 17, 2026
## Explanation Adds the MFA validation layer in `@metamask/profile-sync-controller` that later PRs in this stack consume. - Superstruct schemas and inferred types for auth-service MFA responses, WebAuthn ceremony payloads, and controller-boundary requests (passkey and email OTP only). - `MfaError` family with a stable enumerable `mfaCode`, plus helpers that survive JSON-RPC serialization (`getMfaErrorCode`, `isMfaError`, `getMfaRetryAfterMs`). - Shared `decodeJwtPayload` (no signature verification) and slightly stricter login JWT `exp` handling. - Expanded `HTTP_STATUS_CODES` for later MFA error mapping. This PR does not call MFA HTTP endpoints or change controller behavior. ## References Stacked under [stack #10268](https://github.com/MetaMask/core/pull/10268). Follow-ups: MetaMask#10265, MetaMask#10266, MetaMask#10267. Related to: https://consensyssoftware.atlassian.net/browse/MUL-2260 ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md) - [ ] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches authentication-adjacent validation, JWT claim handling, and structured MFA errors, though runtime sign-in/MFA flows are unchanged until follow-up PRs. > > **Overview** > Introduces the **MFA validation layer** in `@metamask/profile-sync-controller` for later stack PRs—no MFA HTTP calls or controller behavior changes in this diff. > > Adds Superstruct schemas and inferred types under `authentication-jwt-bearer/mfa` for passkey and email OTP flows: auth-service responses, WebAuthn ceremony payloads, enrollment/step-up requests, and **AAL2 elevated-token** claim parsing (including Hydra `ext` nesting). `assertValidMfaRequest` / `assertValidMfaResponse` map validation failures to `MfaError` with path details. > > Expands **`MfaError`** with typed subclasses, stable enumerable **`mfaCode`**, and helpers (`getMfaErrorCode`, `isMfaError`, `getMfaRetryAfterMs`) that work after JSON-RPC serialization. Adds shared **`decodeJwtPayload`** and refactors login JWT expiry checks to use it with stricter `exp` typing. **`HTTP_STATUS_CODES`** gains `401` and `502` for upcoming error mapping. > > Dependencies: **`@metamask/superstruct`** (runtime), **`@metamask/rpc-errors`** (tests). Changelog updated. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit d6d147b. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
from
September 17, 2026 14:16
d418aab to
39856ba
Compare
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
2 times, most recently
from
September 17, 2026 16:32
8e7c721 to
b6ae9e2
Compare
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
from
September 18, 2026 07:23
b6ae9e2 to
62be829
Compare
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
from
September 18, 2026 10:14
62be829 to
98c36cc
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 98c36cc. Configure here.
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
from
September 18, 2026 12:13
98c36cc to
ca7b5d0
Compare
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
2 times, most recently
from
September 18, 2026 13:47
b04a6c3 to
89d2052
Compare
pull Bot
pushed a commit
to Reality2byte/core
that referenced
this pull request
Sep 18, 2026
## Explanation Wires the MFA HTTP client into the SRP JWT auth SDK on top of the validation foundation. - `mfa/services` calls `/api/v2/mfa/*` (enroll, enroll complete, verify, verify complete, credentials), validates request/response bodies, maps server codes to `MfaError` subclasses, and handles OTP cooldown / `Retry-After`. - `SRPJwtBearerAuth` exposes begin/complete enrollment and verification, credential listing, and assertion-to-token exchange. - Public `JwtBearerAuth` forwards those methods and rejects non-SRP auth types. - Nock fixtures and unit tests cover happy paths and error mapping. Clients still go through `AuthenticationController` in later PRs; this layer is not UI-facing. ## References Depends on MetaMask#10264. Follow-ups: MetaMask#10266, MetaMask#10267. Stack: [stack #10268](https://github.com/MetaMask/core/pull/10268). Related to: https://consensyssoftware.atlassian.net/browse/MUL-2261 ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md) - [ ] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **High Risk** > Touches authentication, step-up tokens, and passkey/OTP handling; mistakes could weaken session elevation or mishandle credentials, though coverage is extensive. > > **Overview** > Adds **passkey and email OTP MFA** to the profile-sync JWT auth SDK: a new HTTP layer calls `/api/v2/mfa/*` (enroll, complete, verify, credentials), validates payloads, maps server codes to `MfaError` subclasses, and handles OTP cooldown plus `Retry-After`. > > **`SRPJwtBearerAuth`** and public **`JwtBearerAuth`** expose enrollment/step-up flows, credential listing, and **`exchangeMfaAssertion`** (AAL2 JWT → elevated access token via existing OIDC). MFA is **SRP-only**; email enrollment uses a separate `email` option from `entropySourceId`. > > Schemas mark challenges, OTP codes, passkey payloads, and tokens with **`sensitive()`**. Tests, nock fixtures, changelog, and SDK exports for MFA types are included. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 56b3671. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
from
September 18, 2026 14:40
89d2052 to
af8d930
Compare
mathieuartu
force-pushed
the
mfa/controller-stepup
branch
from
September 18, 2026 21:10
ed5705e to
07823f4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Explanation
Adds MFA step-up verification and a memory-only elevated AAL2 session on
AuthenticationController.beginStepUp,completeStepUp,getElevatedProfileToken,clearStepUpSession.AuthenticationController:stepUpSessionon open/close.getElevatedProfileTokenobserves expiry.operation/credentialTypetags and span attributes for outcome /mfaCode.References
Depends on #10266. Stack: stack #10268.
Related to: https://consensyssoftware.atlassian.net/browse/MUL-2263
Checklist
Note
High Risk
Introduces MFA step-up and in-memory elevated token handling in the authentication controller, including session lifecycle and mid-flight session invalidation—security-sensitive behavior for sensitive operations.
Overview
Adds MFA step-up verification and a short-lived elevated (AAL2) session on
AuthenticationController, with messenger actions forbeginStepUp,completeStepUp,getElevatedProfileToken, andclearStepUpSession.After
completeStepUp, the controller exchanges the MFA assertion for an access token, validates AAL2 JWT claims, and keeps the token in memory only; persisted state exposes onlystepUpSessionExpiresAt(plus configurablestepUpSessionTtlMs, default 60s, clamped to tokenexp).getElevatedProfileTokencan enforce caller freshness viamaxSessionAgeMswithout tearing down the session.Sessions are cleared on wallet lock, sign-out, reset, successful credential enrollment, TTL/token expiry, and explicit
clearStepUpSession. In-flight step-up/enrollment is tied to#authSessionEpochso a lock mid-request cannot reopen an elevated session.Related hardening: MFA
authentication_requiredwhile unlocked invalidates the primary SRP session; MFA tracing records outcome/mfaCodevia trace span attributes instead of mutating tracedata. README, changelog, and broad unit tests cover the new flow.Reviewed by Cursor Bugbot for commit 07823f4. Bugbot is set up for automated code reviews on this repo. Configure here.