Skip to content

fix(seedless-onboarding-controller): fix InvalidPrimarySecretDataType for legacy accounts with client clock skew#9247

Open
huggingbot wants to merge 7 commits into
mainfrom
fix/seedless-promote-primary-srp
Open

fix(seedless-onboarding-controller): fix InvalidPrimarySecretDataType for legacy accounts with client clock skew#9247
huggingbot wants to merge 7 commits into
mainfrom
fix/seedless-promote-primary-srp

Conversation

@huggingbot

@huggingbot huggingbot commented Jun 24, 2026

Copy link
Copy Markdown
Member

Explanation

#fetchAllSecretDataFromMetadataStore sorted results then assumed results[0] was the primary SRP. This assumption breaks for legacy accounts where:

  1. Items pre-dating the dataType migration have no createdAt (server-assigned TIMEUUID), so ordering falls back to the client-side timestamp.
  2. Client clocks can skew — historically the server timestamp was also derived non-uniquely (toUnixTimestamp(NOW()), millisecond resolution), so collisions were possible.
  3. A private key with a skewed-earlier timestamp sorts into results[0], failing the isMnemonic check and throwing InvalidPrimarySecretDataType.

Changes

Instead of hard-failing on results[0], scan the full sorted list for the first item whose dataType is PrimarySrp or unset AND whose SecretType is Mnemonic. Promote that item to index 0 so callers continue to work correctly.

Limitation: SecretType.Mnemonic covers both primary and imported SRPs — the dataType plaintext server field is the only discriminator, and legacy items lack it. If multiple legacy untagged mnemonics exist, the true primary is indistinguishable; we take the oldest by sort order (same heuristic the dataType migration uses). For users already tagged (dataType = PrimarySrp), the sort puts them first anyway so the findIndex short-circuits immediately.

Only throws InvalidPrimarySecretDataType when no mnemonic candidate exists at all — a genuinely unrecoverable state.

References

  • Regression test: should promote the primary SRP to first when a legacy non-mnemonic sorts ahead of it (clock skew)

Checklist

  • Tests written and passing (yarn jest SeedlessOnboardingController)
  • ESLint clean
  • No breaking changes to public API

@huggingbot huggingbot requested a review from a team as a code owner June 24, 2026 05:00
@huggingbot huggingbot changed the title fix(seedless-onboarding-controller): promote primary SRP to handle legacy clock-skew ordering fix(seedless-onboarding-controller): fix InvalidPrimarySecretDataType for legacy accounts with client clock skew Jun 24, 2026
…y SRP fix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@huggingbot huggingbot requested a review from a team as a code owner June 24, 2026 05:17
huggingbot and others added 5 commits June 24, 2026 13:21
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… scenarios

Trace the InvalidPrimarySecretDataType crash to legacy data + clock skew
rather than the dataType migration code:

- legacy primary + legacy private key with skew -> private key in slot 0 (the crash)
- old device adds a private key today -> server createdAt keeps primary in slot 0
- legacy private key still crashes even with a newer createdAt-tagged key present
- untagged primary behind a private key with earlier server createdAt (no skew needed)
- once migration tags PrimarySrp, it always sorts to slot 0 (the cure)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…enarios

Cover collections that mix primary SRP, imported SRP, and imported private
keys written across legacy / old-device / v2 eras:

- legacy private key in slot 0 with the real primary still promotable -> fix recovers
- tagged imported SRP in slot 0 ahead of an untagged primary -> old code crashes, fix recovers
- two legacy mnemonics -> fix recovers but may promote the imported SRP (documents the
  inherent ambiguity, same heuristic the dataType migration uses)
- tagged PrimarySrp wins slot 0 over legacy SRPs and an earliest-timestamp legacy PK
- no mnemonic present -> fix fails closed (still throws)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tic root cause

Drop scenarios that violate the "primary is created first" invariant (a
non-primary item with an earlier createdAt than the primary, a tagged imported
SRP ahead of an untagged primary, a tagged primary beside untagged legacy
siblings) — these are unreachable given addNewSecretData rejects PrimarySrp and
the migration tags the whole collection atomically.

Model the pre-PR validation (results[0] only) to drill into the root cause:

- baseline (no skew): all-legacy collection -> primary in slot 0, old code accepts
- root cause (crash): all-legacy private key skewed into slot 0 -> old code throws
- root cause (silent wrong primary): all-legacy imported SRP skewed into slot 0 ->
  old code accepts it as the primary (legacy mnemonics carry no dataType)
- not the root cause: an item with a real createdAt cannot precede the legacy primary
- not the root cause: a migration-tagged PrimarySrp wins slot 0 regardless of skew

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@grvgoel81

Copy link
Copy Markdown
Contributor

@metamaskbot publish-preview

@github-actions

Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@7.5.3-preview-e80844493
@metamask-previews/accounts-controller@39.0.3-preview-e80844493
@metamask-previews/address-book-controller@7.1.2-preview-e80844493
@metamask-previews/ai-controllers@0.7.0-preview-e80844493
@metamask-previews/analytics-controller@1.1.1-preview-e80844493
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-e80844493
@metamask-previews/announcement-controller@8.1.0-preview-e80844493
@metamask-previews/app-metadata-controller@2.0.1-preview-e80844493
@metamask-previews/approval-controller@9.0.2-preview-e80844493
@metamask-previews/assets-controller@9.0.2-preview-e80844493
@metamask-previews/assets-controllers@109.2.2-preview-e80844493
@metamask-previews/authenticated-user-storage@2.1.0-preview-e80844493
@metamask-previews/base-controller@9.1.0-preview-e80844493
@metamask-previews/base-data-service@0.1.3-preview-e80844493
@metamask-previews/bitcoin-regtest-up@0.0.0-preview-e80844493
@metamask-previews/bridge-controller@76.0.0-preview-e80844493
@metamask-previews/bridge-status-controller@72.2.0-preview-e80844493
@metamask-previews/build-utils@3.0.4-preview-e80844493
@metamask-previews/chain-agnostic-permission@1.6.2-preview-e80844493
@metamask-previews/chomp-api-service@3.1.0-preview-e80844493
@metamask-previews/claims-controller@0.5.3-preview-e80844493
@metamask-previews/client-controller@1.0.1-preview-e80844493
@metamask-previews/compliance-controller@2.1.0-preview-e80844493
@metamask-previews/composable-controller@12.0.1-preview-e80844493
@metamask-previews/config-registry-controller@0.4.1-preview-e80844493
@metamask-previews/connectivity-controller@0.2.0-preview-e80844493
@metamask-previews/controller-utils@12.3.0-preview-e80844493
@metamask-previews/core-backend@6.3.3-preview-e80844493
@metamask-previews/delegation-controller@3.0.2-preview-e80844493
@metamask-previews/earn-controller@12.2.1-preview-e80844493
@metamask-previews/eip-5792-middleware@3.0.4-preview-e80844493
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.1-preview-e80844493
@metamask-previews/eip1193-permission-middleware@2.0.1-preview-e80844493
@metamask-previews/ens-controller@19.1.4-preview-e80844493
@metamask-previews/eth-block-tracker@15.0.1-preview-e80844493
@metamask-previews/eth-json-rpc-middleware@23.1.3-preview-e80844493
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-e80844493
@metamask-previews/foundryup@1.0.1-preview-e80844493
@metamask-previews/gas-fee-controller@26.2.3-preview-e80844493
@metamask-previews/gator-permissions-controller@4.2.1-preview-e80844493
@metamask-previews/geolocation-controller@0.1.3-preview-e80844493
@metamask-previews/java-tron-up@0.0.0-preview-e80844493
@metamask-previews/json-rpc-engine@10.5.0-preview-e80844493
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-e80844493
@metamask-previews/keyring-controller@27.1.0-preview-e80844493
@metamask-previews/local-node-utils@0.0.0-preview-e80844493
@metamask-previews/logging-controller@8.0.2-preview-e80844493
@metamask-previews/message-manager@14.1.2-preview-e80844493
@metamask-previews/messenger@1.2.0-preview-e80844493
@metamask-previews/messenger-cli@0.2.0-preview-e80844493
@metamask-previews/money-account-balance-service@2.1.1-preview-e80844493
@metamask-previews/money-account-controller@0.3.3-preview-e80844493
@metamask-previews/money-account-upgrade-controller@2.1.0-preview-e80844493
@metamask-previews/multichain-account-service@11.0.0-preview-e80844493
@metamask-previews/multichain-api-middleware@3.1.5-preview-e80844493
@metamask-previews/multichain-network-controller@3.1.4-preview-e80844493
@metamask-previews/multichain-transactions-controller@7.1.1-preview-e80844493
@metamask-previews/name-controller@9.1.2-preview-e80844493
@metamask-previews/network-controller@33.0.0-preview-e80844493
@metamask-previews/network-enablement-controller@5.4.0-preview-e80844493
@metamask-previews/notification-services-controller@24.2.0-preview-e80844493
@metamask-previews/passkey-controller@2.0.1-preview-e80844493
@metamask-previews/permission-controller@13.1.1-preview-e80844493
@metamask-previews/permission-log-controller@5.1.0-preview-e80844493
@metamask-previews/perps-controller@8.3.0-preview-e80844493
@metamask-previews/phishing-controller@17.2.0-preview-e80844493
@metamask-previews/polling-controller@16.0.7-preview-e80844493
@metamask-previews/preferences-controller@23.1.0-preview-e80844493
@metamask-previews/profile-metrics-controller@4.0.0-preview-e80844493
@metamask-previews/profile-sync-controller@28.2.0-preview-e80844493
@metamask-previews/ramps-controller@14.3.0-preview-e80844493
@metamask-previews/rate-limit-controller@7.0.1-preview-e80844493
@metamask-previews/react-data-query@0.2.1-preview-e80844493
@metamask-previews/remote-feature-flag-controller@4.2.2-preview-e80844493
@metamask-previews/sample-controllers@5.0.2-preview-e80844493
@metamask-previews/seedless-onboarding-controller@10.0.2-preview-e80844493
@metamask-previews/selected-network-controller@26.1.4-preview-e80844493
@metamask-previews/shield-controller@5.1.2-preview-e80844493
@metamask-previews/signature-controller@39.2.6-preview-e80844493
@metamask-previews/smart-transactions-controller@24.2.3-preview-e80844493
@metamask-previews/snap-account-service@1.0.0-preview-e80844493
@metamask-previews/social-controllers@2.3.1-preview-e80844493
@metamask-previews/solana-test-validator-up@0.0.0-preview-e80844493
@metamask-previews/storage-service@1.0.2-preview-e80844493
@metamask-previews/subscription-controller@6.2.0-preview-e80844493
@metamask-previews/transaction-controller@68.1.1-preview-e80844493
@metamask-previews/transaction-pay-controller@23.14.0-preview-e80844493
@metamask-previews/user-operation-controller@41.2.5-preview-e80844493
@metamask-previews/wallet@4.0.0-preview-e80844493
@metamask-previews/wallet-cli@0.0.0-preview-e80844493

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