Skip to content

[EXTENSION] Sign handlers trust a stale unlock flag after MV3 service-worker restarts — the real vault stays locked while the UI thinks it's unlocked #1356

Description

@wheval

Summary

After a routine Chrome MV3 service-worker restart (happens after ~30s idle, not an edge case), the UI-facing "unlocked" flag is restored from persistent storage, but the actual cryptographic vault lock state is not — every sign handler proceeds as if unlocked, then fails deep inside the SDK instead of cleanly prompting for re-authentication.

Evidence

Full call chain traced:

  • apps/extension-wallet/src/security/storage-manager.ts_storageManager is a plain module-level singleton (let _storageManager: ... = null), recreated fresh (encryptionKey: null) every time the MV3 service worker restarts.
  • packages/core-sdk/src/storage/secure-storage-manager.tsencryptionKey is a private instance field with no persistence/restoration mechanism; only unlock(password) sets it.
  • apps/extension-wallet/src/background/session-state.tsrestoreUnlockSessionFromStorage() runs on every worker boot and restores a separate flag, _sessionUnlocked, from chrome.storage.session (which does survive worker restarts) — but this flag has no link back to the real SecureStorageManager instance's actual lock state.
  • All 5 sign-capable handlers (sign-transaction.ts, sign-message.ts, sign-auth-entry.ts, sign-relay-payload.ts, wallet-state.ts) gate only on isBackgroundSessionUnlocked() (the restored flag) — none check getSharedStorageManager().isUnlocked (a real getter on SecureStorageManager that correctly reflects encryptionKey !== null).
  • Confirmed failure mode: getSigningKeypair()manager.getAccount()getItem()this.assertUnlocked() throws when locked.

Why it matters

The comment at the top of session-state.ts literally states the intent ("MV3 service worker restarts do not force password re-entry on every click") but the actual cryptographic unlock state is never restored to match — only the UI-facing flag is. Users hit a raw thrown error deep in the SDK instead of a clean re-auth prompt, for a completely routine browser behavior (service worker idle-kill).

Suggested fix

Either: (a) gate sign handlers on getSharedStorageManager().isUnlocked in addition to/instead of the flag, treating a mismatch as "needs re-auth" rather than a generic thrown error; or (b) restore the real encryption key on worker boot via a derive-and-cache pattern compatible with chrome.storage.session's in-memory-per-browser-session security properties.

Activity

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

Metadata

Metadata

Assignees

Labels

Stellar WaveIssues in the Stellar wave program

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions