Suite: let members switch between positions they hold
A member holding #2 and #3 connected #3's wallet, closed the browser, came back, and still saw #2. Not a wallet problem: the sign-in cookie lasts 30 days and there was no sign-out anywhere on the site, so the first position he authenticated as was pinned to that browser and the wallet was never consulted again. This hits precisely the people we tell to buy several positions - the Triple Play is on /how-pay-works and in the chatbot - so it will keep happening. Adds POST /api/public/signout (drops the server session and expires the cookie) and a "not this position? switch" link beside the identity line on /suite. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+16
-1
@@ -181,4 +181,19 @@ function adminList() {
|
||||
return getMessages().slice(-300).reverse().map(m => ({ mid: m.mid, fromId: m.fromId, toId: m.toId || null, org: !!m.org, body: m.body, ts: m.ts, readCount: Object.keys(m.read || {}).length }));
|
||||
}
|
||||
|
||||
module.exports = { init, makeChallenge, verifyChallenge, mintSession, authFromCookie, sessionCookie, send, inbox, markRead, unreadCount, adminList, ADDR_RE };
|
||||
// Drop a session. The sign-in cookie is persistent (30 days), so closing the
|
||||
// browser does NOT end it - a member holding several positions who connects a
|
||||
// different wallet keeps getting served the position they first signed in as.
|
||||
function clearSession(req) {
|
||||
// authFromCookie returns the session VALUE, not its key - read the token
|
||||
// straight off the cookie so the server-side entry really goes away.
|
||||
try {
|
||||
const m = /(?:^|;\s*)ctb\.msid=([^;]+)/.exec(req.headers.cookie || '');
|
||||
if (m) sessions.delete(decodeURIComponent(m[1]));
|
||||
} catch (e) {}
|
||||
}
|
||||
function clearCookie() {
|
||||
return `ctb.msid=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0${IS_PROD ? '; Secure' : ''}`;
|
||||
}
|
||||
|
||||
module.exports = { init, makeChallenge, verifyChallenge, mintSession, authFromCookie, sessionCookie, clearSession, clearCookie, send, inbox, markRead, unreadCount, adminList, ADDR_RE };
|
||||
|
||||
@@ -83,12 +83,20 @@
|
||||
$('suMe').style.display = 'block';
|
||||
$('suMe').innerHTML = '<b>#' + me.id + '</b> — you’re early! The Circle Suite is in <b>team beta</b> right now. Your license is already reserved by your position, and every tool below activates for you the moment we open the doors. Nothing to do — watch the team channel.';
|
||||
$('suConnect').style.display = 'none';
|
||||
var sw = document.getElementById('suSwitch');
|
||||
if (sw) sw.addEventListener('click', async function (ev) {
|
||||
ev.preventDefault();
|
||||
sw.textContent = 'signing out…';
|
||||
try { await fetch('/api/public/signout', { method: 'POST' }); } catch (e) {}
|
||||
location.reload();
|
||||
});
|
||||
me = null; pips(); render();
|
||||
return true;
|
||||
}
|
||||
$('suMe').style.display = 'block';
|
||||
$('suMe').innerHTML = me.inOrg
|
||||
? '<b>#' + me.id + '</b> · ' + me.tierName + ' · Level ' + me.level + ' (' + me.levelName + ')' +
|
||||
' <a href="#" id="suSwitch" style="color:var(--teal);font-size:13px;margin-left:8px">not this position? switch</a>' +
|
||||
(me.directCount >= 2 ? ' · <span style="color:var(--teal)">✓ qualified</span>' : ' · ' + me.directCount + '/2 directs') +
|
||||
'<br>Your license covers <b>Level ' + me.level + '</b> and everything below it' +
|
||||
(me.level < 8 ? ' — your next upgrade extends it to ' + LEVELS[me.level] + '.' : ' — full Suite.')
|
||||
|
||||
@@ -703,6 +703,14 @@ async function handleApi(req,res,pathname){
|
||||
return out;
|
||||
}
|
||||
|
||||
// Sign out / switch position. Needed because one person can hold several
|
||||
// positions (the Triple Play we actively recommend), and without this the
|
||||
// 30-day cookie pins them to whichever one they signed in as first.
|
||||
if(req.method==='POST'&&pathname==='/api/public/signout'){
|
||||
try{ messages.clearSession(req); }catch(e){}
|
||||
return json(res,200,{ok:true},{'Set-Cookie':messages.clearCookie()});
|
||||
}
|
||||
|
||||
// ── Circle Suite entitlement helper (shared by suite-me and the tools) ────
|
||||
async function suiteEntitlement(req){
|
||||
const s=messages.authFromCookie(req);
|
||||
|
||||
Reference in New Issue
Block a user