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:
@@ -0,0 +1,47 @@
|
||||
// Outbound mail via SendGrid v3 (domain-authenticated instantadpay.com).
|
||||
// Key sources: SENDGRID_KEY env, else DATA_DIR/sendgrid.key in the volume.
|
||||
// No key = email sign-in stays feature-flagged off in production.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
|
||||
let DATA_DIR = null;
|
||||
const FROM = { email: 'no-reply@instantadpay.com', name: 'InstantAdPay' };
|
||||
|
||||
function init(opts) { DATA_DIR = opts.dataDir; }
|
||||
function key() {
|
||||
if (process.env.SENDGRID_KEY) return process.env.SENDGRID_KEY.trim();
|
||||
try { return fs.readFileSync(path.join(DATA_DIR, 'sendgrid.key'), 'utf8').trim(); } catch (e) { return ''; }
|
||||
}
|
||||
function hasKey() { return !!key(); }
|
||||
|
||||
function send(to, subject, text) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const body = JSON.stringify({
|
||||
personalizations: [{ to: [{ email: to }] }],
|
||||
from: FROM,
|
||||
subject,
|
||||
content: [{ type: 'text/plain', value: text }]
|
||||
});
|
||||
const req = https.request({ hostname: 'api.sendgrid.com', path: '/v3/mail/send', method: 'POST',
|
||||
headers: { Authorization: 'Bearer ' + key(), 'Content-Type': 'application/json',
|
||||
'Content-Length': Buffer.byteLength(body) }, timeout: 15000 },
|
||||
res => {
|
||||
let d = '';
|
||||
res.on('data', c => d += c);
|
||||
res.on('end', () => res.statusCode < 300 ? resolve(true) : reject(new Error('sendgrid ' + res.statusCode + ': ' + d.slice(0, 200))));
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.on('timeout', () => req.destroy(new Error('sendgrid timeout')));
|
||||
req.end(body);
|
||||
});
|
||||
}
|
||||
|
||||
function sendCode(to, code) {
|
||||
return send(to, code + ' is your InstantAdPay sign-in code',
|
||||
'Your sign-in code is: ' + code + '\n\n'
|
||||
+ 'It works for 15 minutes. If you did not request it, ignore this email.\n\n'
|
||||
+ 'InstantAdPay\nAdvertise and earn instantly. Locked in code, not promises.\nhttps://instantadpay.com');
|
||||
}
|
||||
|
||||
module.exports = { init, hasKey, send, sendCode };
|
||||
Reference in New Issue
Block a user