083
LVL 03 — MID DEVELOPERSESSION 083DAY 83

SENDING EMAILS

🎫 PIXELCRAFT-070
Feature | 🟡 Medium | Priority: 🟠 High

Users forget their passwords and have no way to recover access. Build a "Forgot Password" flow: enter email → receive reset link → set new password. Token expires in 1 hour.
CONCEPTS.UNLOCKED
📧
Transactional Email
Automated emails triggered by user actions. Password resets, welcome emails, upload confirmations. Not marketing — one-to-one, event-driven. Use Resend, SendGrid, or Nodemailer.
🔑
Password Reset Flow
Request → token → email → verify → reset. User submits email. Server generates a secure token, stores its hash, emails the link. User clicks link, provides new password. Server verifies token, updates password.
🎨
Email Templates
HTML emails with dynamic data. Templates with placeholders for user name, reset link, expiry time. Email HTML is different from web HTML — inline styles, table layouts, limited CSS.
🔐
Secure Token Generation
crypto.randomBytes(32) for unpredictable tokens. Store the hash (not the token itself) in the database. The token in the email is the only way to prove identity — it must be cryptographically random.
Token Expiration
Tokens must expire. Store expiresAt alongside the hash. 1 hour is standard for password resets. After expiry, the token is useless — the user must request a new one.
🛡️
Security Considerations
Don't leak user existence. Always say "If an account exists, we sent an email" — never "Email not found." Rate-limit the endpoint. Invalidate token after use.
HANDS-ON.TASKS
01
Set Up Email Service
npm install resend // services/email.js const { Resend } = require('resend'); const resend = new Resend( process.env.RESEND_API_KEY); async function sendEmail({ to, subject, html }) { const { data, error } = await resend.emails.send({ from: 'PixelCraft <noreply@pixelcraft.app>', to, subject, html, }); if (error) throw error; return data; } module.exports = { sendEmail };
02
Request Password Reset
const crypto = require('crypto'); router.post('/auth/forgot-password', validate(forgotPasswordSchema), async (req, res) => { const user = await User.findOne({ email: req.body.email }); // Always respond the same way // (don't leak user existence) if (!user) { return res.json({ message: 'If an account exists, ' + 'we sent a reset email.' }); } // Generate secure token const token = crypto.randomBytes(32).toString('hex'); const tokenHash = crypto.createHash('sha256') .update(token).digest('hex'); // Store hash + expiry in DB user.resetTokenHash = tokenHash; user.resetTokenExpires = new Date(Date.now() + 60 * 60 * 1000); await user.save(); // Send email with raw token const resetUrl = `${process.env.FRONTEND_URL}` + `/reset-password?token=${token}`; await sendEmail({ to: user.email, subject: 'Reset your password', html: resetPasswordTemplate({ name: user.name, resetUrl }), }); res.json({ message: 'If an account exists, ' + 'we sent a reset email.' }); });
Store the HASH of the token, not the token itself. If the database is compromised, attackers can't use the hashes to reset passwords. Same principle as password hashing.
03
Reset Password with Token
router.post('/auth/reset-password', validate(resetPasswordSchema), async (req, res) => { const { token, newPassword } = req.body; // Hash the provided token const tokenHash = crypto.createHash('sha256') .update(token).digest('hex'); // Find user with matching // unexpired token const user = await User.findOne({ resetTokenHash: tokenHash, resetTokenExpires: { $gt: new Date() }, }); if (!user) { return res.status(400).json({ error: 'Invalid or expired token' }); } // Update password user.password = await bcrypt.hash(newPassword, 12); // Invalidate the token user.resetTokenHash = undefined; user.resetTokenExpires = undefined; await user.save(); res.json({ message: 'Password reset successful' }); });
04
Email Template
function resetPasswordTemplate({ name, resetUrl }) { return ` <div style=" font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; "> <h2 style="color: #1a1a2e;"> Reset Your Password </h2> <p>Hi ${name},</p> <p>Click the button below to reset your password. This link expires in 1 hour.</p> <a href="${resetUrl}" style=" display: inline-block; background: #e94560; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; margin: 16px 0; ">Reset Password</a> <p style="color: #666; font-size: 14px;"> If you didn't request this, ignore this email. </p> </div> `; }
05
Close the Ticket
git switch -c feature/PIXELCRAFT-070-password-reset git add server/ src/ git commit -m "Add password reset flow with email (PIXELCRAFT-070)" git push origin feature/PIXELCRAFT-070-password-reset # PR → Review → Merge → Close ticket ✅
CS.DEEP-DIVE

Email is the oldest and most critical internet protocol still in use.

SMTP was defined in 1982 — before the web existed. It's still how every email is delivered.

// The email delivery chain:

Your server
  → SMTP to email service (Resend)
  → DNS MX lookup (find recipient's server)
  → SPF check (is sender authorized?)
  → DKIM check (is email unmodified?)
  → DMARC policy (what to do if fails?)
  → Recipient's inbox

// Why use a service (not raw SMTP)?
Deliverability — avoid spam filters
IP reputation — shared warm IPs
Bounce handling — track failures
Analytics — opens, clicks, bounces

// Email is deceptively complex.
// Use a service. Trust the experts.
"Email Lab"
[A]Add a welcome email: when a user registers, send a branded welcome email with a "Verify Email" button. Track email verification status in the user model.
[B]Build a weekly digest email: use a scheduled BullMQ job (from session 079) to send each user a summary of their uploads, most popular images, and storage usage.
[C]Research: why is HTML email still stuck in 2005? Why do email clients use different rendering engines? Explore MJML or React Email for building responsive email templates.
REF.MATERIAL
ARTICLE
Resend
Modern email API: send transactional emails, React Email templates, delivery tracking, and webhook events. Developer-first email service.
RESENDOFFICIALESSENTIAL
VIDEO
Hussein Nasser
Deep dive into SMTP, MX records, SPF, DKIM, and DMARC. How an email actually gets from sender to inbox through the internet.
EMAILPROTOCOL
ARTICLE
OWASP Foundation
Security best practices for password reset: token generation, expiry, rate limiting, and preventing user enumeration.
SECURITYOFFICIALESSENTIAL
ARTICLE
Resend Team
Build email templates with React components. Preview in the browser, render to HTML. Modern DX for the oldest web technology.
REACTEMAIL
VIDEO
Fireship
Quick overview of email in Node.js: Nodemailer, SendGrid, Resend, and building transactional email flows.
EMAILQUICK
// LEAVE EXCITED BECAUSE
Click "Forgot Password" → check your inbox → click the link → set a new password. Secure, time-limited, and professionally templated. Your app handles the most critical user flow in authentication.