Passwordless sign-in: 6-digit email codes, feature-flagged on SendGrid key
/api/auth/email/start issues a 15-min code (60s resend guard, 6 tries); verify creates the account passwordless (sponsor cookie first-touch) and mints the session. UI swaps the password cards for the code flow when config.emailAuth is on; dev mode returns the code inline. Password flow remains until the key lands in the volume (data/sendgrid.key) or SENDGRID_KEY env. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+13
-1
@@ -68,6 +68,18 @@ function login(email, password) {
|
||||
acct.lastSeen = Date.now(); save();
|
||||
return { ok: true, account: publicView(acct) };
|
||||
}
|
||||
// Passwordless path: a verified email code proves ownership, so the account
|
||||
// may exist with no password at all.
|
||||
function ensure(email, sponsorId) {
|
||||
const e = normEmail(email);
|
||||
if (!EMAIL_RE.test(e)) return { error: 'That email address does not look right.' };
|
||||
if (!db.byEmail[e]) {
|
||||
db.byEmail[e] = { email: e, pass: null, sponsorId: Number(sponsorId) || 0, address: null, created: Date.now() };
|
||||
db.joins += 1;
|
||||
save();
|
||||
}
|
||||
return { ok: true, account: publicView(db.byEmail[e]) };
|
||||
}
|
||||
function byEmail(email) { const a = db.byEmail[normEmail(email)]; return a ? publicView(a) : null; }
|
||||
function byAddress(address) {
|
||||
const e = db.byAddress[normAddr(address)];
|
||||
@@ -96,4 +108,4 @@ function publicView(a) {
|
||||
}
|
||||
function count() { return Object.keys(db.byEmail).length; }
|
||||
|
||||
module.exports = { init, signup, login, byEmail, byAddress, linkWallet, count };
|
||||
module.exports = { init, signup, login, ensure, byEmail, byAddress, linkWallet, count };
|
||||
|
||||
Reference in New Issue
Block a user