Prompt
How do I send a password reset email from a backend API?
Latest observation
To send a password reset email from a backend API, the usual flow is:
-
User requests a reset
- They submit their email address to an endpoint like
POST /auth/forgot-password.
- They submit their email address to an endpoint like
-
Backend verifies the account exists
- If the email is registered, generate a secure random reset token.
- Store a hashed version of that token in your database, along with:
- user ID
- expiration time
- whether it’s been used
-
Send an email with a reset link
- The email should contain a link like:
https://your-frontend.com/reset-password?token=...
- The token in the URL should be the raw token, not the hash.
- The email should contain a link like:
-
User clicks the link and sets a new password
- Frontend sends the token + new password to something like
POST /auth/reset-password.
- Frontend sends the token + new password to something like
-
Backend validates the token
- Check:
- token exists
- token not expired
- token not used
- If valid, update the user’s password and invalidate the token.
- Check:
Example backend flow
1) Request reset endpoint
POST /auth/forgot-password
Input:
{ "email": "user@example.com" }
Backend:
- find user
- generate token
- save hashed token + expiry
- send email
2) Generate token securely
In Node.js:
import crypto from "crypto";
const token = crypto.randomBytes(32).toString("hex");
const tokenHash = crypto.createHash("sha256").update(token).digest("hex");
Store tokenHash, not token.
3) Send the email
Use an email provider like:
- SendGrid
- Mailgun
- SES
- Postmark
- Nodemailer with SMTP
Example email content:
Click here to reset your password:
https://yourapp.com/reset-password?token=RAW_TOKEN
Important security practices
- Never send the password in email
- Never store reset tokens in plain text
- Make tokens expire quickly, e.g. 15–60 minutes
- Make tokens single-use
- Return a generic response even if the email doesn’t exist, so attackers can’t enumerate accounts:
"If the email exists, we sent a reset link."
- Use HTTPS
- Rate-limit reset requests
- Invalidate existing sessions after password reset
Example API responses
For POST /auth/forgot-password:
{ "message": "If the email exists, a reset link has been sent." }
For POST /auth/reset-password:
{ "message": "Password reset successful." }
Minimal Node.js example
import crypto from "crypto";
app.post("/auth/forgot-password", async (req, res) => {
const { email } = req.body;
const user = await db.users.findOne({ email });
if (!user) {
return res.json({ message: "If the email exists, a reset link has been sent." });
}
const token = crypto.randomBytes(32).toString("hex");
const tokenHash = crypto.createHash("sha256").update(token).digest("hex");
await db.passwordResets.insertOne({
userId: user._id,
tokenHash,
expiresAt: new Date(Date.now() + 1000 * 60 * 30), // 30 min
used: false
});
const resetUrl = `https://yourapp.com/reset-password?token=${token}`;
await sendEmail(user.email, "Reset your password", `
Click this link to reset your password:
${resetUrl}
`);
res.json({ message: "If the email exists, a reset link has been sent." });
});
If you want, I can also show you:
- a full Node/Express example
- a NestJS example
- a Python/FastAPI example
- or how to do this with JWT reset tokens
Observation history
Unlock observation history
Create a free Obsurfable account to unlock historical responses, model comparisons, and deeper brand intelligence.
How did Obsurfable measure this prompt?
Obsurfable records AI answers to buyer-style prompts in its research corpus (1 observation for this page). Metrics are distributions over observations, not a single static ranking.
Which AI systems does Obsurfable collect answers from?
OpenAI, ChatGPT, Google, Gemini, Google AI Mode, Anthropic, Claude, Perplexity, Grok, DeepSeek, Mistral, Copilot, and Meta AI.