Skip to content

feat: decide the Core Kit storage scope and gate its key set - #1174

Merged
FSM1 merged 3 commits into
mainfrom
feat/web-auth-derived-state
Aug 8, 2026
Merged

feat: decide the Core Kit storage scope and gate its key set#1174
FSM1 merged 3 commits into
mainfrom
feat/web-auth-derived-state

Conversation

@FSM1

@FSM1 FSM1 commented Aug 8, 2026

Copy link
Copy Markdown
Owner

The decision

Keep the Web3Auth Core Kit store origin-wide (window.localStorage), and bound the
window with an explicit sessionTime instead of the storage scope.

The issue's default was sessionStorage, on the reading that the one-leader tab model makes
cross-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.ts promote()provideFailoverSecret()
    SecretSource.provideSecret(), which throws outright without a source.
  • apps/web/src/engine/loginHandoff.ts LoginSecretSource.provideSecret calls
    _UNSAFE_exportTssKey() on this tab's Core Kit; nothing else can answer, and the secret
    never crosses the BroadcastChannel.
  • apps/web/src/auth/CoreKitProvider.tsx builds a session and calls restore() per tab, and
    apps/web/src/auth/useAuth.ts arms the secret source only after that tab's own handoff.

Under sessionStorage, any tab the user did not personally log in through (a bookmark, a
second 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_SECONDS is
therefore its only other bound, and it is now set deliberately.

sessionTime turned out to bound two things, which is what set the value: the held session
record's TTL, and session_token_exp_second on the signatures a re-export needs. Two hours
would 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 store
standing, including a device factor share once MFA is reachable. Web3AuthSession.logout()
now clears the store itself, on the refused path too.

Gates

  • Unit (apps/web/src/auth/coreKit.test.ts): the store is origin-wide, the session ceiling is
    truthy and under the SDK default — truthiness matters, because the SDK restores 86400s on a
    falsy value — and logout clears the store.
  • web-e2e (tests/web-e2e/tests/smoke.spec.ts): a full login and cold start leave nothing in
    Web 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.setItem to 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

  • Sets sessionTime to 28,800 seconds (8 hours) on Web3AuthMPCCoreKit construction via a new SESSION_SECONDS constant.
  • Binds the SDK to window.localStorage for origin-wide storage scope.
  • Adds a clearStore() helper to Web3AuthSession that removes the SDK's persisted entry (keyed by coreKit._storageKey) from the provided Storage instance.
  • Store clearing now runs on all session-ending paths: logout (via try/finally), and failed/partial logins that don't reach LOGGED_IN.
  • Adds unit tests in coreKit.test.ts and an e2e smoke test asserting only corekit_store and loglevel keys remain in storage after login.

Macroscope summarized 39c5515.

Summary by CodeRabbit

  • Improvements

    • Improved session persistence and restoration across browser restarts.
    • Sessions now remain active for up to eight hours before requiring renewal.
    • Logout reliably clears saved sign-in data, even if a sign-out request encounters an error.
    • Startup behavior now limits browser storage to approved authentication data and avoids unnecessary session storage entries.
  • Tests

    • Added coverage for session persistence, restoration timing, logout cleanup, and storage behavior during login.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Core 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.

Changes

Core Kit storage lifecycle

Layer / File(s) Summary
Session storage initialization
apps/web/src/auth/coreKit.ts, apps/web/src/auth/coreKit.test.ts
Web3AuthMPCCoreKit uses window.localStorage, an eight-hour sessionTime, and the same storage instance as Web3AuthSession. Unit tests verify these settings.
Logout cleanup and storage validation
apps/web/src/auth/coreKit.ts, tests/web-e2e/tests/smoke.spec.ts
Logout always removes the Core Kit storage entry. The end-to-end test validates the approved localStorage keys and empty sessionStorage.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

  • FSM1/cipher-box issue 1175: It also changes Core Kit storage persistence and logout behavior, extending this storage foundation with encrypted asynchronous storage.

Possibly related PRs

  • FSM1/cipher-box#899: Integrates Core Kit login handoff and lifecycle behavior related to logout cleanup.
  • FSM1/cipher-box#911: Introduces the Core Kit authentication adapter that this change updates.
  • FSM1/cipher-box#1122: Modifies Core Kit session restoration and timeout handling in the same authentication area.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address issue #913 by documenting localStorage scope, setting an 8-hour session lifetime, clearing storage on logout, and gating persisted keys in web-e2e tests.
Out of Scope Changes check ✅ Passed The unit tests, logout cleanup, storage configuration, and web-e2e allow-list all support the objectives in issue #913.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: defining Core Kit storage scope and restricting its persisted key set.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/web-auth-derived-state

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

FSM1 and others added 2 commits August 8, 2026 13:06
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>
@FSM1
FSM1 force-pushed the feat/web-auth-derived-state branch from a111ae6 to 0300e3a Compare August 8, 2026 11:06
@FSM1
FSM1 marked this pull request as ready for review August 8, 2026 11:43

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between cfe5a68 and 0300e3a.

📒 Files selected for processing (3)
  • apps/web/src/auth/coreKit.test.ts
  • apps/web/src/auth/coreKit.ts
  • tests/web-e2e/tests/smoke.spec.ts

Comment thread apps/web/src/auth/coreKit.test.ts Outdated
Comment thread apps/web/src/auth/coreKit.test.ts
Comment thread apps/web/src/auth/coreKit.ts
Comment thread apps/web/src/auth/coreKit.ts
Comment thread tests/web-e2e/tests/smoke.spec.ts Outdated
@FSM1
FSM1 marked this pull request as draft August 8, 2026 11:48
…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>
@FSM1
FSM1 marked this pull request as ready for review August 8, 2026 12:08
@FSM1
FSM1 merged commit 390a579 into main Aug 8, 2026
34 checks passed
@FSM1
FSM1 deleted the feat/web-auth-derived-state branch August 8, 2026 12:09
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.

web: decide the Core Kit storage scope and gate its key set

1 participant