Email-first membership: signup/login, wallet linked at purchase time
Normal people join with email + password (sponsor attribution via cookie at
signup); the wallet only appears when buying or activating payouts, and gets
linked to the account then. Wallet-only sign-in remains for crypto-native
users. Sessions carry {email, address, memberId}.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -134,7 +134,28 @@ const server = http.createServer(async (req, res) => {
|
||||
return json(res, 200, { onchainMembers: members, siteAccounts: accounts.count() });
|
||||
}
|
||||
|
||||
// -- auth
|
||||
// -- accounts: email + password is the normal join path (wallet comes
|
||||
// out only at purchase / payout-activation time and gets linked then)
|
||||
if (p === '/api/signup' && req.method === 'POST') {
|
||||
const b = await readBody(req);
|
||||
const sid = Number(parseCookies(req)['iap.sponsor']) || 0; // first-touch attribution
|
||||
const r = accounts.signup(b.email, b.password, sid);
|
||||
if (r.error) return json(res, 400, r);
|
||||
const token = auth.mintSession({ email: r.account.email });
|
||||
return json(res, 200, { ok: true, account: r.account }, { 'Set-Cookie': auth.sessionCookie(token) });
|
||||
}
|
||||
if (p === '/api/login' && req.method === 'POST') {
|
||||
const b = await readBody(req);
|
||||
const r = accounts.login(b.email, b.password);
|
||||
if (r.error) return json(res, 400, r);
|
||||
let memberId = 0;
|
||||
if (r.account.address) { try { memberId = await chain.memberIdByAccount(r.account.address); } catch (e) {} }
|
||||
const token = auth.mintSession({ email: r.account.email, address: r.account.address, memberId });
|
||||
return json(res, 200, { ok: true, account: r.account }, { 'Set-Cookie': auth.sessionCookie(token) });
|
||||
}
|
||||
|
||||
// -- wallet auth: link-to-account when an email session exists, or
|
||||
// wallet-first sign-in for crypto-native users
|
||||
if (p === '/api/auth/challenge' && req.method === 'POST') {
|
||||
const b = await readBody(req);
|
||||
const r = auth.makeChallenge(b.address);
|
||||
@@ -144,12 +165,19 @@ const server = http.createServer(async (req, res) => {
|
||||
const b = await readBody(req);
|
||||
const r = await auth.verifyChallenge(b.address, b.signature);
|
||||
if (r.error) return json(res, 400, r);
|
||||
// bind the visitor's sponsor cookie to this wallet, first touch wins
|
||||
const sid = Number(parseCookies(req)['iap.sponsor']) || 0;
|
||||
const attributed = accounts.attributeSponsor(r.address, sid);
|
||||
accounts.upsert(r.address, { lastSeen: Date.now() });
|
||||
return json(res, 200, { ok: true, address: r.address, memberId: r.memberId, sponsorId: attributed },
|
||||
{ 'Set-Cookie': auth.sessionCookie(r.token) });
|
||||
let memberId = 0;
|
||||
try { memberId = await chain.memberIdByAccount(r.address); } catch (e) {}
|
||||
const s = auth.fromRequest(req);
|
||||
if (s && s.email) {
|
||||
const lr = accounts.linkWallet(s.email, r.address);
|
||||
if (lr.error) return json(res, 400, lr);
|
||||
auth.updateSession(s.token, { address: r.address, memberId });
|
||||
return json(res, 200, { ok: true, linked: true, address: r.address, memberId });
|
||||
}
|
||||
const acct = accounts.byAddress(r.address);
|
||||
const token = auth.mintSession({ email: acct ? acct.email : null, address: r.address, memberId });
|
||||
return json(res, 200, { ok: true, address: r.address, memberId },
|
||||
{ 'Set-Cookie': auth.sessionCookie(token) });
|
||||
}
|
||||
if (p === '/api/auth/logout' && req.method === 'POST') {
|
||||
auth.logout(req);
|
||||
@@ -159,8 +187,10 @@ const server = http.createServer(async (req, res) => {
|
||||
const s = auth.fromRequest(req);
|
||||
if (!s) return json(res, 200, { signedIn: false });
|
||||
const memberId = await auth.refreshMemberId(s);
|
||||
const acct = accounts.get(s.address) || {};
|
||||
const out = { signedIn: true, address: s.address, memberId, sponsorId: acct.sponsorId || 0 };
|
||||
const acct = (s.email && accounts.byEmail(s.email)) || (s.address && accounts.byAddress(s.address)) || null;
|
||||
const out = { signedIn: true, email: s.email || (acct && acct.email) || null,
|
||||
address: s.address || (acct && acct.address) || null, memberId,
|
||||
sponsorId: (acct && acct.sponsorId) || Number(parseCookies(req)['iap.sponsor']) || 0 };
|
||||
if (memberId) {
|
||||
try {
|
||||
const mm = await chain.member(memberId);
|
||||
|
||||
Reference in New Issue
Block a user