feat(auth): sign-in-with-wallet — adopt an existing wallet-linked account instead of erroring

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).
This commit is contained in:
jwinterm
2026-07-10 09:35:08 -04:00
parent 679fda93ce
commit 7e7e66e887
2 changed files with 28 additions and 4 deletions
+10
View File
@@ -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($('<li class="status">').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.
+18 -4
View File
@@ -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.'
});
}