Prompt
How do I send password reset emails from an API?
Latest observation
Sep 30, 2026
Grok
Send password reset emails from an API by generating a short-lived, single-use secure token, storing a hashed version of it, and emailing a reset link that includes the token.
Core flow
- User requests reset POST /api/password-reset with { "email": "user@example.com" }.
- API side
- Look up the user by email (always respond with a generic success message to avoid account enumeration).
- Generate a cryptographically secure random token (e.g. 32+ bytes from a CSPRNG).
- Hash the token (SHA-256 or better) and store the hash + expiry (typically 15–60 minutes) against the user.
- Build a reset URL: https://yourapp.com/reset-password?token=PLAIN_TOKEN.
- Send an email containing that link (and optionally a short code).
- User clicks the link / submits new password POST /api/password-reset/confirm with { "token": "...", "newPassword": "..." }.
- API validates
- Hash the incoming token and look it up.
- Check it exists, hasn’t expired, and hasn’t been used.
- Update the user’s password (hash it with a strong algorithm such as bcrypt/Argon2).
- Invalidate the token (delete it or mark it used).
- Optionally invalidate other sessions / require re-login.
Practical implementation notes
Token generation (Node.js example)
JavaScript
import crypto from 'crypto';
const token = crypto.randomBytes(32).toString('hex');
const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
// store tokenHash + expiresAt
- Use a transactional email provider (SendGrid, Postmark, Amazon SES, Resend, Mailgun, etc.).
- Keep the email simple: clear subject, the reset link, expiry time, and a note that the request can be ignored if the user didn’t initiate it.
- Prefer HTML + plain-text versions.
- Never put the plain password in the email.
Security best practices
- Always use HTTPS for the reset link.
- Rate-limit the request endpoint (per IP and per email).
- Make tokens single-use and short-lived.
- Hash tokens before storing them.
- Do not reveal whether an email address is registered.
- After a successful reset, consider logging the user out of other devices or rotating session tokens.
- Log the event for security auditing (without logging the token itself).
- Protect against timing attacks when looking up the token.
Framework / library helpers
- Node/Express: crypto + a mail library (nodemailer, @sendgrid/mail, resend).
- Python/Django: built-in password-reset views + email backend, or Django REST Framework + custom token model.
- Python/FastAPI: generate token with secrets, store in DB, send via fastapi-mail or similar.
- Ruby on Rails: has_secure_token or Devise’s recoverable module.
- Go: crypto/rand + any SMTP or provider SDK.
Minimal API surface
| Endpoint | Method | Purpose |
|---|---|---|
| /password-reset | POST | Request reset email |
| /password-reset/confirm | POST | Submit token + new password |
| (optional) /password-reset/verify | GET | Check whether a token is still valid |
Most production systems also add a “change password while logged in” flow that does not use email tokens.
If you tell me your stack (Node, Python, Go, etc.) and which email provider you prefer, I can give concrete code samples.