Suite: force the wallet account picker when switching position

Clearing our session was only half the problem. eth_requestAccounts returns
whichever account the wallet already has connected to the site - it never
opens the picker - so a member holding two positions could sign out, switch
accounts in MetaMask, reconnect, and be handed the same address again. Signing
with it resolved to the same position, which looked exactly like the switch
doing nothing.

This is the trap already documented in our own Triple Play guidance, and the
Triple Play is what puts members in this situation in the first place.

Switching now calls wallet_requestPermissions first, which forces the account
selector open. Only on switch, so ordinary single-position sign-ins are
unchanged. And if the wallet still returns the same address, the page now says
so - naming the address and pointing at the wallet's connected-sites list -
instead of silently redisplaying the same position.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-08-31 15:21:44 -05:00
parent 2d1ccadfd9
commit 5978ef139f
+27
View File
@@ -88,6 +88,8 @@
ev.preventDefault();
sw.textContent = 'signing out…';
try { await fetch('/api/public/signout', { method: 'POST' }); } catch (e) {}
// Tell connect() to force the wallet's account picker on the way back in.
try { sessionStorage.setItem('rmc.switch', '1'); } catch (e) {}
location.reload();
});
me = null; pips(); render();
@@ -114,15 +116,40 @@
try {
var eth = await window.RMCWallet.pick();
if (!eth) { {var _ia=(window.RMCInApp&&window.RMCInApp.notice('/suite'))||'';if(_ia){err.innerHTML=_ia;if(err.style)err.style.display='block';window.RMCInApp.bind('/suite');}else{err.textContent = 'This browser has no wallet in it. Open this page INSIDE your wallet app instead: open Trust, MetaMask, SafePal or Coinbase, find its Browser (or DApps) tab, and type rmcircle.team/suite into that address bar. On iPhone, Trust no longer has a browser — use MetaMask or SafePal, or ask your sponsor to link you instead.';}} err.style.display = 'block'; btn.disabled = false; btn.textContent = '🔑 Connect wallet — light up my tools'; return; }
// eth_requestAccounts SILENTLY returns whichever account the site is
// already connected to - it never opens the picker. So someone holding
// several positions can switch accounts in MetaMask all day and still be
// handed the first one they ever connected, sign with it, and resolve to
// the same position. Asking for permissions first forces the picker open.
var wantSwitch = false;
try { wantSwitch = sessionStorage.getItem('rmc.switch') === '1'; } catch (e) {}
if (wantSwitch) {
try { await eth.request({ method: 'wallet_requestPermissions', params: [{ eth_accounts: {} }] }); }
catch (pe) { /* wallet may not support it; fall through to the normal request */ }
try { sessionStorage.removeItem('rmc.switch'); } catch (e) {}
}
var accs = await eth.request({ method: 'eth_requestAccounts' });
var account = accs[0];
var shortAcc = account ? account.slice(0, 6) + '…' + account.slice(-4) : '';
try { await window.RMCWallet.ensureChain(eth); } catch (ce) {}
var ch = await (await fetch('/api/public/msg-challenge', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ address: account }) })).json();
if (!ch.message) throw new Error(ch.error || 'Could not start sign-in.');
var sig = await eth.request({ method: 'personal_sign', params: [ch.message, account] });
var vr = await (await fetch('/api/public/msg-verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ address: account, signature: sig }) })).json();
if (!vr.ok) throw new Error(vr.error || 'Signature check failed.');
var prevId = null;
try { prevId = sessionStorage.getItem('rmc.lastId'); } catch (e) {}
await refreshMe();
try {
var nowId = (me && me.id) ? String(me.id) : null; // `me` is set by refreshMe() in this same closure
if (nowId) sessionStorage.setItem('rmc.lastId', nowId);
if (prevId && nowId && prevId === nowId) {
err.innerHTML = 'Your wallet handed back the same address (<b>' + shortAcc + '</b>), so this is still position <b>#' + nowId +
'</b>. Wallets keep a site connected to one account: open your wallet, find its connected-sites or permissions list, ' +
'disconnect <b>rmcircle.team</b>, then tap switch here again and pick the other account.';
err.style.display = 'block';
}
} catch (e) {}
} catch (e) {
var m = String((e && e.message) || e);
err.textContent = /not registered|no position/i.test(m)