feat: decide the Core Kit storage scope and gate its key set - #1174
Conversation
WalkthroughCore Kit now uses explicit local-storage persistence with an eight-hour session duration. Logout removes persisted Core Kit state even when SDK logout fails. Unit and end-to-end tests validate initialization, cleanup, and allowed browser storage keys. ChangesCore Kit storage lifecycle
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Keep the Web3Auth Core Kit store origin-wide and record why: a tab promoted to leader on failover re-exports the login secret from its own restored Core Kit session, and `EngineClient.promote` has no other source, so a per-tab `sessionStorage` store would leave every tab that did not itself log in unable to stand the engine up. Cap the bearer window with an explicit two-hour `sessionTime` instead of the SDK's 24-hour default. Gate the accepted key set two ways: a unit test over the options the SDK is constructed with, and a web-e2e assertion that a full login, cold start and reload leave nothing in Web Storage outside the allow-list. Closes #913 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The session ceiling bounds `session_token_exp_second` as well as the held session record, so it also caps how long a tab can re-export the login secret on a failover promotion — two hours would have made a mid-session promotion fail silently. Set it against both effects instead. Clear Core Kit's store on logout: the SDK blanks its session id in place and leaves everything else it wrote standing, a device factor share included once MFA is reachable. State what the store actually holds, assert the session ceiling is truthy — the SDK restores its own default on a falsy value — and name the whole accepted key set in the e2e allow-list. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
a111ae6 to
0300e3a
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/auth/coreKit.test.ts`:
- Around line 47-53: Add test cases around createCoreKitSession and logout
covering both an SDK logout rejection and an already-logged-out session state;
configure the mock per case, seed corekit_store, and assert it is removed in
each branch. For the rejection case, also assert that logout propagates the
expected error while still performing cleanup.
- Around line 38-44: Update the test case around createCoreKitSession to assert
that built.options?.sessionTime exactly equals 28_800, replacing the broader
greater-than/less-than range assertions while preserving the existing session
restoration setup.
In `@apps/web/src/auth/coreKit.ts`:
- Around line 105-111: Replace the window.localStorage-backed store in the
follower-promotion flow around EngineClient.promote with a non-exportable or
user-mediated credential mechanism. Ensure the secp256k1 bearer material is
never persisted in localStorage, sessionStorage, or other Web Storage while
preserving cross-tab promotion behavior.
- Around line 77-85: Update the failed-login rollback in login() to remove the
Core Kit store in a finally path around the direct this.coreKit.logout() call,
even when logout rejects and the error is suppressed. Reuse the cleanup behavior
already implemented by logout() rather than leaving partial-session data behind.
In `@tests/web-e2e/tests/smoke.spec.ts`:
- Around line 68-73: Update the ALLOWED_STORAGE_KEY regular expression to match
only the exact loglevel and corekit_store keys, using non-capturing grouping and
an end anchor; preserve the existing allow-list scope and do not broaden the
test beyond meaningful validation.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: e3db34d2-d9a1-4ab6-b7e2-bb6de064f91d
📒 Files selected for processing (3)
apps/web/src/auth/coreKit.test.tsapps/web/src/auth/coreKit.tstests/web-e2e/tests/smoke.spec.ts
…y allow-list A failed login ends the partial session through the SDK and swallows a refusal, so a residual store entry — a stale session id now, a device factor share once MFA is reachable — survived the rollback. Both teardown paths now go through one clearStore. The e2e allow-list anchored only corekit_store, so loglevel-anything passed the unexpected-key gate. loglevel persists one key per named logger, so the entry is a namespace, not a literal: allow loglevel and loglevel:<name>, exactly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The decision
Keep the Web3Auth Core Kit store origin-wide (
window.localStorage), and bound thewindow with an explicit
sessionTimeinstead of the storage scope.The issue's default was
sessionStorage, on the reading that the one-leader tab model makescross-tab session restore moot. It does not. A follower tab that is promoted to leader
re-exports the login secret from its own restored Core Kit session, and that is the only
source there is:
packages/client/src/engineClient.tspromote()→provideFailoverSecret()→SecretSource.provideSecret(), which throws outright without a source.apps/web/src/engine/loginHandoff.tsLoginSecretSource.provideSecretcalls_UNSAFE_exportTssKey()on this tab's Core Kit; nothing else can answer, and the secretnever crosses the BroadcastChannel.
apps/web/src/auth/CoreKitProvider.tsxbuilds a session and callsrestore()per tab, andapps/web/src/auth/useAuth.tsarms the secret source only after that tab's own handoff.Under
sessionStorage, any tab the user did not personally log in through (a bookmark, asecond window) restores nothing, renders signed out, and — worse — cannot stand the engine up
when the leader dies, so the promotion aborts and the origin loses its engine.
The cost accepted
The store stays readable by any same-origin script for as long as it exists. The value in it
is a secp256k1 scalar that both addresses and decrypts a Web3Auth-held record carrying the
shares an export needs — bearer key material, not an opaque handle.
SESSION_SECONDSistherefore its only other bound, and it is now set deliberately.
sessionTimeturned out to bound two things, which is what set the value: the held sessionrecord's TTL, and
session_token_exp_secondon the signatures a re-export needs. Two hourswould have capped the working session at two hours — a mid-session failover promotion would
have failed, silently, over a UI still rendering signed in. Eight hours outlasts a working
day while still not surviving a night on a shared machine, which the SDK's 86400s default
does.
The remaining exposure — an offline-replayable capability in script-readable storage — wants a
sealed store under a non-extractable IndexedDB key. That is filed separately.
Also in this diff
Core Kit's
logout()blanks its session id in place and leaves the rest of its storestanding, including a device factor share once MFA is reachable.
Web3AuthSession.logout()now clears the store itself, on the refused path too.
Gates
apps/web/src/auth/coreKit.test.ts): the store is origin-wide, the session ceiling istruthy and under the SDK default — truthiness matters, because the SDK restores 86400s on a
falsy value — and logout clears the store.
tests/web-e2e/tests/smoke.spec.ts): a full login and cold start leave nothing inWeb Storage outside the named allow-list. Deliberately scoped: the suite signs in through
the introspection hook and this build carries no Web3Auth credentials, so Core Kit never
constructs — what it gates is that nothing else in the app starts persisting. Proven red by
adding one
localStorage.setItemto the bundle.The unit test is the gate on the storage decision itself; the e2e is the gate on anything new
appearing beside it.
Closes #913
Note
Gate Core Kit storage scope and clear store on session-ending paths
sessionTimeto 28,800 seconds (8 hours) onWeb3AuthMPCCoreKitconstruction via a newSESSION_SECONDSconstant.window.localStoragefor origin-wide storage scope.clearStore()helper toWeb3AuthSessionthat removes the SDK's persisted entry (keyed bycoreKit._storageKey) from the providedStorageinstance.try/finally), and failed/partial logins that don't reachLOGGED_IN.corekit_storeandloglevelkeys remain in storage after login.Macroscope summarized 39c5515.
Summary by CodeRabbit
Improvements
Tests