From 0a86b012d69847ed48bd5aedd756f20ec7b48e51 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Wed, 9 Sep 2026 01:15:14 +0200 Subject: [PATCH] fix(sirius-x): sign-in Done unblocks portal; session survives reload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two issues reported after an Import sign-in: 1. Done button was stuck — user saw the ✓ Signed in screen but the portal never switched to the names view. Root cause: finishSignIn fired siriusProfileChanged BEFORE setting window.siriusWallet, so the portal's listener called adoptWalletFromModal() while window.siriusWallet was still null, saw no wallet, and did nothing. Fix: expose the live wallet BEFORE writeProfile so the sync listener sees it and can enterPortal() immediately. 2. Every reload asked for the password again. Cache the wallet's mnemonic in sessionStorage on sign-in — same tab (or a page reload) rebuilds the BuiltInWallet silently via BuiltInWallet.fromMnemonic; a full browser close clears sessionStorage and the user is back at the Unlock tab. sessionStorage is per-origin per-tab so an XSS on Sirius.X pages would still be able to read it — that's the tradeoff for the convenience. Chipnet only; mainnet gets the PIN escrow pattern (3 wrong PIN tries → escalate to password) that Digibyte.x/web already uses. PIN implementation is deferred to its own commit. portal.html adoptWalletFromModal now tries sessionStorage after the in-memory check; register-flow.js startFlow/startTldFlow do the same via a new async adoptSessionWalletAsync so name and TLD mints on any page reuse the session wallet with no re-prompt. profile-menu.js sign-out clears sessionStorage + window.siriusWallet so signing out really does drop the user. --- js/profile-menu.js | 5 +++++ js/register-flow.js | 42 ++++++++++++++++++++++++++++++++++-------- portal.html | 14 ++++++++++++++ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/js/profile-menu.js b/js/profile-menu.js index ca252c1..d2650d0 100644 --- a/js/profile-menu.js +++ b/js/profile-menu.js @@ -155,6 +155,11 @@ const action = t.dataset.action; if (action === "signout") { try { localStorage.removeItem("siriusProfile"); } catch {} + // Also drop the session-cached seed so a reload after sign-out really + // signs the user out (else the next page load would silently rebuild + // the same wallet from sessionStorage). + try { sessionStorage.removeItem("siriusSessionMnemonic"); sessionStorage.removeItem("siriusSessionSource"); } catch {} + try { delete window.siriusWallet; } catch { window.siriusWallet = null; } window.dispatchEvent(new Event("siriusProfileChanged")); // Reload so any signed-in section on the current page (portal, tld) resets. location.reload(); diff --git a/js/register-flow.js b/js/register-flow.js index 702e8e7..9ed1e83 100644 --- a/js/register-flow.js +++ b/js/register-flow.js @@ -242,11 +242,20 @@ function writeProfile(w) { // mode, mark siriusProfile and close the modal here; otherwise return // false so the caller keeps going to Fund → Confirm → Register. function finishSignIn(w) { - writeProfile({ address: w.address, tokenAddress: w.tokenAddress, source: w.source ?? "seed" }); - // Expose the live wallet globally so the portal / TLD page can pick it up - // without re-prompting for a password. In-memory only — cleared on reload, - // sign-out, or navigation away. + // Expose the live wallet globally BEFORE writeProfile so any listener of + // siriusProfileChanged (portal.html adoptWalletFromModal) sees the wallet + // and can switch to the signed-in view synchronously. window.siriusWallet = w; + // Session-cache the seed so a page reload restores the wallet without + // asking for the password again. sessionStorage is per-tab and clears on + // browser close — same lifetime as a "keep me signed in for this session" + // toggle in a normal app. Chipnet-only convenience; mainnet gets the PIN + // escrow pattern (see finishSignIn comment history). + try { + if (w?.mnemonic) sessionStorage.setItem("siriusSessionMnemonic", w.mnemonic); + if (w?.source === "wc") sessionStorage.setItem("siriusSessionSource", "wc"); + } catch {} + writeProfile({ address: w.address, tokenAddress: w.tokenAddress, source: w.source ?? "seed" }); if (state.signInOnly) { render(`

Signed in

You are signed in as ${esc(w.address)}. Names you own @@ -337,7 +346,24 @@ function adoptSignedInWallet() { return false; } -function startFlow(name, opts = {}) { +// Best-effort silent rebuild from sessionStorage — same session ⇒ same +// wallet, no password prompt. Returns true when the wallet is ready. +async function adoptSessionWalletAsync() { + if (state.wallet) return true; + if (adoptSignedInWallet()) return true; + try { + const cached = typeof sessionStorage !== "undefined" + ? sessionStorage.getItem("siriusSessionMnemonic") + : null; + if (!cached) return false; + const w = await BNS.BuiltInWallet.fromMnemonic(cached); + window.siriusWallet = w; + state.wallet = w; + return true; + } catch { return false; } +} + +async function startFlow(name, opts = {}) { state.name = name; state.tld = null; state.signInOnly = false; @@ -350,7 +376,7 @@ function startFlow(name, opts = {}) { ? BigInt(opts.serviceFeeSats) : (p ? p.sats : null); open(); - if (adoptSignedInWallet()) stepFund(); + if (await adoptSessionWalletAsync()) stepFund(); else stepWallet(); } @@ -358,12 +384,12 @@ function startFlow(name, opts = {}) { // the mint step calls registerTldWith(Built|External)Wallet instead of the // name variant, and the "done" screen skips the record editor since a TLD // certificate goes to the operator's token address as-is. -function startTldFlow(label, opts = {}) { +async function startTldFlow(label, opts = {}) { state.name = `.${label}`; // used for headings only state.tld = { label, serviceFeeSats: BigInt(opts.serviceFeeSats ?? 0n) }; state.signInOnly = false; open(); - if (adoptSignedInWallet()) stepFund(); + if (await adoptSessionWalletAsync()) stepFund(); else stepWallet(); } diff --git a/portal.html b/portal.html index 71ca787..c69665f 100644 --- a/portal.html +++ b/portal.html @@ -278,6 +278,20 @@ async function adoptWalletFromModal() { await enterPortal(); return true; } + // No live wallet in memory — but if a session mnemonic is cached from an + // earlier sign-in in this browser session, rebuild the wallet silently. + // Same tab (or a reload) keeps the user signed in; a full browser close + // clears sessionStorage and drops the user back to Unlock. + try { + const cached = sessionStorage.getItem("siriusSessionMnemonic"); + if (cached) { + const w = await BNS.BuiltInWallet.fromMnemonic(cached); + wallet = w; + window.siriusWallet = w; + await enterPortal(); + return true; + } + } catch { /* fall through to sign-in prompt */ } return false; } function readProfile() {