diff options
| author | Adam Soltys <[email protected]> | 2026-08-17 22:44:37 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-08-17 22:44:37 -0700 |
| commit | 2d1f1e425fa2fc3103ca1f426c4ca278f7c9cd91 (patch) | |
| tree | bb38bc84a446bfd837d3f084d7074fdcc76b3484 /src | |
| parent | 0ee50c5f64feb9419f799fb53c99b4b852e52452 (diff) | |
| download | coinosv3-2d1f1e425fa2fc3103ca1f426c4ca278f7c9cd91.tar.gz coinosv3-2d1f1e425fa2fc3103ca1f426c4ca278f7c9cd91.zip | |
The login identity stays with the wallet that earned it: a live signer no longer follows you into wallets that never linked it, where it claimed names, wore crowns, and answered to the wrong name
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JtaatFfAbx91Nh3xqKVqKz
Diffstat (limited to 'src')
| -rw-r--r-- | src/features/nostrlogin.js | 17 |
1 files changed, 16 insertions, 1 deletions
diff --git a/src/features/nostrlogin.js b/src/features/nostrlogin.js index 66fd8a2..6c06fc8 100644 --- a/src/features/nostrlogin.js +++ b/src/features/nostrlogin.js @@ -32,6 +32,9 @@ export function nostrLoginFeature(ctx) { // the user's real npub as their payment address (and nominate the wallet // key as manager, so later updates work without the signer). let live = null; + // true while a sign-in is mid-flight: the signer is live but its link to + // the wallet hasn't been persisted yet, so init() must not sever it + let attaching = false; let resuming = null; let resumeFailedAt = 0; @@ -72,7 +75,7 @@ export function nostrLoginFeature(ctx) { }; const busy = (v) => { ui.nostrLoginBusy = v; render(); }; - const fail = (e) => { ui.nostrLoginError = e.message || String(e); busy(false); }; + const fail = (e) => { attaching = false; ui.nostrLoginError = e.message || String(e); busy(false); }; // Open the wallet a signer identifies. New accounts confirm first, because // publishing an encrypted seed to relays deserves an explicit yes. @@ -93,9 +96,11 @@ export function nostrLoginFeature(ctx) { // so an extension login later finds this wallet instead of making a new // one. Costs nothing — anyone with the key could derive it anyway. if (res.publish) await publishWalletBackup(signer, { mnemonic: res.mnemonic }).catch(() => {}); + attaching = true; live = selfHealing(signer); await ctx.openMnemonic(res.mnemonic, res.passphrase || '', { nostrPubkey: signer.pubkey }); save({ ...load(), pubkey: signer.pubkey, linked: Date.now(), ...sessionOf(signer) }); + attaching = false; // claim the real npub as the payment address while this signer is live ctx.hook('namesAdoptIdentity', signer, npubOf(signer.pubkey))?.catch?.(() => {}); ctx.onbNostrLogin(res.mode === 'restored'); @@ -116,9 +121,11 @@ export function nostrLoginFeature(ctx) { const mnemonic = existing ? existing.mnemonic : newMnemonic(); if (!existing) await publishWalletBackup(st.signer, { mnemonic }); ui.nostrLoginNew = null; + attaching = true; live = selfHealing(st.signer); await ctx.openMnemonic(mnemonic, (existing && existing.passphrase) || '', { nostrPubkey: st.signer.pubkey }); save({ ...load(), pubkey: st.signer.pubkey, linked: Date.now(), ...sessionOf(st.signer) }); + attaching = false; ctx.hook('namesAdoptIdentity', st.signer, npubOf(st.signer.pubkey))?.catch?.(() => {}); ctx.onbNostrLogin(!!existing); toast(existing ? t('nlOpenedRestored') : t('nlCreated')); @@ -385,6 +392,14 @@ export function nostrLoginFeature(ctx) { return { id: 'nostrlogin', stop() { stopNostrConnect(); }, + init() { + // A live signer is bound to the wallet that linked it. Switching to an + // account that never did must not inherit the login identity — it used + // to, and the fresh wallet then claimed names under the login npub, + // showed its profile, and even wore its hats. (Mid-sign-in the link + // isn't saved yet; `attaching` keeps this from severing it.) + if (live && !attaching && load().pubkey !== live.pubkey) live = null; + }, // The nostr identity this session is logged in as, for features that // should speak as the user (the payment address defaults to this npub). nostrLoginIdentity() { |
