From 7e7e66e88716e99d62d536a595f796287af3c337 Mon Sep 17 00:00:00 2001 From: jwinterm Date: Fri, 10 Jul 2026 09:35:08 -0400 Subject: [PATCH] =?UTF-8?q?feat(auth):=20sign-in-with-wallet=20=E2=80=94?= =?UTF-8?q?=20adopt=20an=20existing=20wallet-linked=20account=20instead=20?= =?UTF-8?q?of=20erroring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A Smirk wallet that already owns another account no longer fails with 'already linked to another account'. Ownership is cryptographically proven, so it's a login: the server re-points that account to the current socket and returns its session token; the client persists it and reloads to re-establish as that account. Fixes a returning user being unable to sign in from a fresh browser session. NOTE: touches the identity/session path — verify live (sign in with a wallet already linked to another account -> lands on that account with its address/credits). --- html/js/network/smirkAuth.js | 10 ++++++++++ src/routes/auth.js | 22 ++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/html/js/network/smirkAuth.js b/html/js/network/smirkAuth.js index 35073e1..454ec0a 100644 --- a/html/js/network/smirkAuth.js +++ b/html/js/network/smirkAuth.js @@ -97,6 +97,16 @@ const SmirkAuth = { provenPublicKey = result.pubkey; } + // Sign-in-with-wallet adoption: if this wallet already owned an account, the server signed + // us into it and returned that account's session token. Persist it and reload so the whole + // session re-establishes as that account (credits, address, history). + if (verifyData && verifyData.adopted && verifyData.sessionToken) { + try { localStorage.setItem('wownerogue_token', verifyData.sessionToken); } catch (_) { /* ignore */ } + $('#messages').append($('
  • ').text('🔑 Signed in to your wallet-linked account. Reloading…')); + setTimeout(function () { window.location.reload(); }, 700); + return { success: true, linked: true, adopted: true, address: verifyData.address || null }; + } + // Step 3: Get wallet addresses and auto-set payout address // Note: the proven public key is a pubkey (hex), NOT an address. // We need to call getAddresses() to get the actual WOW address. diff --git a/src/routes/auth.js b/src/routes/auth.js index ed6a021..278823f 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -46,14 +46,28 @@ module.exports = function createAuthRoutes(ctx) { const userId = userResult.rows[0].id; - // Reject linking a wallet already bound to a different account. + // Sign-in-with-wallet: if this proven wallet already owns a DIFFERENT account, that is a + // LOGIN to that account, not a hijack (ownership is cryptographically proven). Adopt it — + // re-point that user to the current socket and hand the client its session token so the + // browser re-establishes as that account. (Previously this threw "already linked to another + // account", which blocked a returning user from signing in on a fresh browser session.) const existingLink = await db.query(` - SELECT id FROM users WHERE smirk_public_key = $1 AND id != $2 + SELECT id, anon_token, payout_address FROM users WHERE smirk_public_key = $1 AND id != $2 LIMIT 1 `, [provenKey, userId]); if (existingLink.rows.length > 0) { - throw new ValidationError('Wallet already linked', { - safeMessage: 'This wallet is already linked to another account.' + const owner = existingLink.rows[0]; + await db.query( + `UPDATE users SET socket_id = $1, last_seen = NOW() WHERE id = $2`, + [socketId, owner.id] + ); + return res.json({ + success: true, + linked: true, + adopted: true, + sessionToken: owner.anon_token || null, + address: owner.payout_address || null, + message: 'Signed in to your wallet-linked account.' }); }