Skip to content

feat(keyring-eth-money-mpc): skip MFA for whitelisted signatures - #670

Draft
matthiasgeihs wants to merge 40 commits into
mainfrom
mg/money-mpc-keyring
Draft

matthiasgeihs wants to merge 40 commits into
mainfrom
mg/money-mpc-keyring

Conversation

@matthiasgeihs

@matthiasgeihs matthiasgeihs commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Skip MFA for personal messages, typed data, and EIP-7702 authorizations that @metamask/money-account-mfa-whitelist allows, using getMfaWhitelistConfig for the pinned config.
  • Send the original signature request with the hash on the first /sign. If the backend responds with { "code": "mfa_required" }, request a 2FA profile token and call /sign once more without the request.
  • Key generation, import, share rotation, share sync, and transaction signing still always use a 2FA token. A missing or throwing whitelist config fails closed to MFA.

@metamask/money-account-mfa-whitelist is consumed from the preview build @metamask-previews/money-account-mfa-whitelist@0.0.0-preview-6ccf7a055. Replace that with a semver range once the package is published.

Test plan

  • Sign a whitelisted personal message and confirm getProfileToken is called with twoFactor: false and /sign includes the request.
  • Return { "code": "mfa_required" } from /sign and confirm the keyring prompts for 2FA and retries once with a new nonce and no request.
  • Confirm a second mfa_required, or mfa_required after a token that already has 2FA, throws and does not open a signing session.
  • Confirm transactions, key generation, import, rotation, and sync still request a 2FA token.
  • yarn workspace @metamask/eth-money-mpc-keyring test

matthiasgeihs and others added 30 commits September 2, 2026 15:41
Introduce a 2-party client/server MPC keyring with DKLS23 TSS, cloud backup sync, and vendored MFA wallet libs.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Pass convertV so recovery id 27|28 becomes yParity or EIP-155 v.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace backupId with shareEpoch so create/rotate append and activate
epochs only after backup readiness, matching the robust MPC architecture.

Co-authored-by: Cursor <cursoragent@cursor.com>
Prevent mid-sign rotate/sync from being overwritten by stale

Co-authored-by: Cursor <cursoragent@cursor.com>
#applyKeyState snapshots from concurrent signing.
Pass the create/rotate session nonce to storeKeyShareBackup so a
delayed backup from an overwritten retry cannot attach to a newer share.

Co-authored-by: Cursor <cursoragent@cursor.com>
<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?

Are there any issues or other links reviewers should consult to
understand this pull request better? For instance:

* Fixes #12345
* See: #67890
-->

## Summary

- MPC cloud HTTP calls now send the profile token in an `Authorization:
Bearer <token>` header instead of including `token` in JSON request
bodies.
- The shared `postJson` helper accepts the token as a separate argument
and applies it consistently across all MPC backend requests.
- Token-only endpoints (`getNetId`, `checkKeyShareBackupId`,
`loadKeyShareBackup`) still use POST but now send an empty JSON body
(`{}`).

## Test plan

- [x] Run `packages/keyring-eth-mpc` unit tests (`cloud.test.ts`)
- [x] Verify `getNetId` requests include the Bearer header and send `{}`
as the body
- [x] Verify sign and backup helpers include the Bearer header and omit
`token` from the JSON payload
- [ ] Confirm MPC backend accepts Bearer authentication for affected
endpoints before merge

<!--
Are there any examples of this change being used in another repository?

When considering changes to the MetaMask module template, it's strongly
preferred that the change be experimented with in another repository
first. This gives reviewers a better sense of how the change works,
making it less likely the change will need to be reverted or adjusted
later.
-->


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Auth moves from JSON to Bearer headers across all MPC cloud calls;
clients and the backend must agree before deploy or requests will fail.
> 
> **Overview**
> MPC cloud HTTP clients no longer put the profile `token` in JSON
bodies. **`postJson`** now takes the token separately and sets
**`Authorization: Bearer <token>`** on every backend POST.
> 
> Endpoints that previously sent only `{ token }` (**`getNetId`**,
**`checkKeyShare`**, **`loadKeyShareBackup`**) now POST **`{}`** with
auth in the header. Create/sign/rotate/backup/register calls keep their
payloads but drop the **`token`** field. Unit tests in
**`cloud.test.ts`** assert the header and that **`body.token`** is
absent.
> 
> The unreleased **CHANGELOG** entry is shortened to a single
initial-release line (detailed feature bullets removed).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
400e5b1. 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: Matthias Geihs <matthias.geihs@consensys.net>
<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?

Are there any issues or other links reviewers should consult to
understand this pull request better? For instance:

* Fixes #12345
* See: #67890
-->

## Summary

- Add a shared `parseJsonResponse` helper in
`packages/keyring-eth-mpc/src/cloud.ts` to centralize JSON parsing and
non-OK error handling for cloud API responses.
- Introduce `getJson` for Bearer-authenticated GET requests and refactor
`postJson` to reuse `parseJsonResponse`.
- Update `loadKeyShareBackup` to call `getJson` with GET instead of POST
with an empty body.

<!--
Are there any examples of this change being used in another repository?

When considering changes to the MetaMask module template, it's strongly
preferred that the change be experimented with in another repository
first. This gives reviewers a better sense of how the change works,
making it less likely the change will need to be reverted or adjusted
later.
-->


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes the HTTP method for loading encrypted key-share backups;
behavior depends on the MPC backend accepting GET on that route.
> 
> **Overview**
> Refactors MPC cloud HTTP helpers and fixes how encrypted key-share
backups are fetched.
> 
> **`cloud.ts`** adds **`parseJsonResponse`** for shared non-OK handling
and JSON/empty-body parsing, introduces **`getJson`** for
Bearer-authenticated GETs, and routes **`postJson`** through the same
parser (generic renamed to **`Result`**). **`loadKeyShareBackup`** now
calls **`getJson`** on `/load-key-share-backup` instead of
**`postJson`** with an empty body.
> 
> **`cloud.test.ts`** asserts the backup load uses **GET** with
**`Authorization: Bearer`** and no POST body.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
2690e54. 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: Matthias Geihs <matthias.geihs@consensys.net>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- Implemented a new function `waitForDoneStatus` to ensure the backend
has completed the create and rotate operations by checking for a
`status: done` message.
- Updated the `MPCKeyring` class to call this function after initiating
create and rotate sessions.
- Enhanced tests to verify behavior when receiving non-done status
messages during the create process.

<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?

Are there any issues or other links reviewers should consult to
understand this pull request better? For instance:

* Fixes #12345
* See: #67890
-->

## Examples

<!--
Are there any examples of this change being used in another repository?

When considering changes to the MetaMask module template, it's strongly
preferred that the change be experimented with in another repository
first. This gives reviewers a better sense of how the change works,
making it less likely the change will need to be reverted or adjusted
later.
-->

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes ordering and failure modes for MPC key creation, rotation, and
signing against the backend; mistakes could block wallet setup or drop
signatures, though signing is explicitly lenient.
> 
> **Overview**
> MPC key create and share rotation now **block until the cloud peer
sends a `status` message with payload `done`**, via new
`waitForDoneStatus`, before disconnecting and continuing with backup
storage / epoch activation. If the backend reports anything else (e.g.
`running`), **create and rotate fail** with `Expected status done,
received …`, so backups are not written when persistence is incomplete.
> 
> **Signing** still requests the same status after TSS sign, but
**non-`done` status is ignored** so a late backend signal does not
discard an already-computed signature (signing does not mutate shares).
Tests and the changelog document this split behavior.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
d8b464a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
A backend that never responds would otherwise hold the keyring mutex until the client restarts.

Co-authored-by: Cursor <cursoragent@cursor.com>
A failed sign may have consumed the cached setup, so the next sign rebuilds it with the peer.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the 2-of-2 threshold next to the other share constants.

Co-authored-by: Cursor <cursoragent@cursor.com>
Match the Dkls23-style casing. The persisted keyring type string stays 'MPC Keyring'.

Co-authored-by: Cursor <cursoragent@cursor.com>
Public instance methods already had JSDoc. This covers the class, static type, and constructor.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Reject a drifted cloud body or malformed keyring state before it is used.

Co-authored-by: Cursor <cursoragent@cursor.com>
Run init inside #serializeOp with the initialized check inside the queue, so overlapping init calls run key generation or import once. The second call waits, sees the initialized state and returns.

Co-authored-by: Cursor <cursoragent@cursor.com>
The SDK dropped the -lib suffix and the util package. The keyring now uses the network package's identity serializer and scoped session id.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the V1 MPC keyring implementation together with the V2 adapter already on main.

Co-authored-by: Cursor <cursoragent@cursor.com>
Changes required after the changes made to the MFA SDK.
Map relative .js specifiers back to TypeScript sources so the MPC keyring tests can load. Also point the skipLibCheck note at the MFA network package.

Co-authored-by: Cursor <cursoragent@cursor.com>
matthiasgeihs and others added 4 commits October 12, 2026 17:50
…etup mode

A payload with invalid initialized fields and mode was accepted as setup because extra keys are ignored, which dropped the key material.

Co-authored-by: Cursor <cursoragent@cursor.com>
Drop publicToAddressHex and compare recovered keys with publicKeyToAddressHex, which accepts raw and SEC1 keys.

Co-authored-by: Cursor <cursoragent@cursor.com>
Reuse the shared AES-GCM helpers for key-share backup encryption.

Co-authored-by: Cursor <cursoragent@cursor.com>
@matthiasgeihs
matthiasgeihs requested a review from a team as a code owner October 13, 2026 14:43
@socket-security

socket-security Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​metamask-previews/​money-account-mfa-whitelist@​0.0.0-preview-6ccf7a055761009490100

View full report

@matthiasgeihs
matthiasgeihs marked this pull request as draft October 13, 2026 15:22
…ounts (#669)

## Summary
- Rename the package from `keyring-eth-mpc` /
`@metamask/eth-mpc-keyring` to `keyring-eth-money-mpc` /
`@metamask/eth-money-mpc-keyring`.
- Rename the V1 and V2 keyring classes to `MpcMoneyKeyring`, and the
controller type to `MPC Money Keyring`.
- Update the related public types, source filenames, and monorepo
references (README, tsconfig, Yarn constraints, PR title scope).

## Test plan
- [ ] `yarn workspace @metamask/eth-money-mpc-keyring test`
- [ ] Confirm the controller still registers the keyring under `MPC
Money Keyring`
- [ ] Confirm `KeyringType.Mpc` and
`KeyringAccountEntropyTypeOption.Mpc` are unchanged


Made with [Cursor](https://cursor.com)

Co-authored-by: Cursor <cursoragent@cursor.com>
Base automatically changed from mg/rename-mpc-keyring to mg/mpc-keyring October 13, 2026 19:24
matthiasgeihs and others added 5 commits October 13, 2026 21:31
Money accounts do not sign transactions, so drop signTransaction and the helpers that only existed for it.

Co-authored-by: Cursor <cursoragent@cursor.com>
Ask the whitelist before prompting, and retry once with a 2FA token when the backend still returns mfa_required.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Base automatically changed from mg/mpc-keyring to main October 14, 2026 12:41

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants