DEV Community

Cover image for Implementing "Forgot Password" with MongoDB: A Step-by-Step Guide
pwnkdm
pwnkdm

Posted on

2 1 1 1 1

Implementing "Forgot Password" with MongoDB: A Step-by-Step Guide

A "Forgot Password" feature is essential for any user authentication system. It enhances user experience and security by allowing users to reset their passwords safely. In this guide, we’ll walk through implementing this feature using MongoDB, Node.js, Express, and JWT (JSON Web Token).

Prerequisites
Before we begin, ensure you have:

  • Node.js & npm installed
  • MongoDB set up (local or cloud-based like MongoDB Atlas)
  • Basic knowledge of Express & JWT

Step 1: Set Up Your Project
Initialize a new Node.js project:

mkdir forgot-password && cd forgot-password
npm init -y
Enter fullscreen mode Exit fullscreen mode

Install required dependencies:

npm install express mongoose dotenv jsonwebtoken bcryptjs nodemailer cors body-parser
Enter fullscreen mode Exit fullscreen mode

Step 2: Configure MongoDB & Express
Create an .env file to store database credentials:

MONGO_URI=your_mongodb_connection_string
JWT_SECRET=your_jwt_secret
EMAIL_USER=your_email@example.com
EMAIL_PASS=your_email_password

Enter fullscreen mode Exit fullscreen mode

Set up Express and MongoDB connection in server.js:

const express = require("express");
const mongoose = require("mongoose");
require("dotenv").config();

const app = express();
app.use(express.json());

mongoose.connect(process.env.MONGO_URI)
    .then(() => console.log("MongoDB Connected"))
    .catch(err => console.error(err));

app.listen(5000, () => console.log("Server running on port 5000"));

Enter fullscreen mode Exit fullscreen mode

Step 3: Create the User Model
Define a User schema in models/User.js:

const mongoose = require("mongoose");

const UserSchema = new mongoose.Schema({
    email: { type: String, required: true, unique: true },
    password: { type: String, required: true },
    resetToken: { type: String, default: null },
    resetTokenExpiry: { type: Date, default: null }
});

module.exports = mongoose.model("User", UserSchema);

Enter fullscreen mode Exit fullscreen mode

Step 4: Implement the Password Reset Flow
1️⃣ Generate Reset Token
Create an endpoint in routes/auth.js to send a reset email:

const express = require("express");
const User = require("../models/User");
const jwt = require("jsonwebtoken");
const nodemailer = require("nodemailer");

const router = express.Router();

router.post("/forgot-password", async (req, res) => {
    const { email } = req.body;
    const user = await User.findOne({ email });

    if (!user) return res.status(404).json({ message: "User not found" });

    const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: "1h" });

    user.resetToken = token;
    user.resetTokenExpiry = Date.now() + 3600000; // 1 hour
    await user.save();

    // Email setup
    const transporter = nodemailer.createTransport({
        service: "gmail",
        auth: {
            user: process.env.EMAIL_USER,
            pass: process.env.EMAIL_PASS
        }
    });

    const mailOptions = {
        to: user.email,
        subject: "Password Reset",
        text: `Click the link to reset your password: http://localhost:3000/reset-password/${token}`
    };

    await transporter.sendMail(mailOptions);

    res.json({ message: "Reset link sent to email" });
});

module.exports = router;

Enter fullscreen mode Exit fullscreen mode

2️⃣ Reset Password
Create an endpoint to reset the password:

router.post("/reset-password", async (req, res) => {
    const { token, newPassword } = req.body;

    try {
        const decoded = jwt.verify(token, process.env.JWT_SECRET);
        const user = await User.findOne({ _id: decoded.id, resetToken: token });

        if (!user || user.resetTokenExpiry < Date.now()) {
            return res.status(400).json({ message: "Invalid or expired token" });
        }

        user.password = require("bcryptjs").hashSync(newPassword, 10);
        user.resetToken = null;
        user.resetTokenExpiry = null;
        await user.save();

        res.json({ message: "Password reset successful" });
    } catch (err) {
        res.status(400).json({ message: "Invalid token" });
    }
});

module.exports = router;

Enter fullscreen mode Exit fullscreen mode

Step 5: Test the Implementation
Send a POST request to /forgot-password with { "email": "user@example.com" }.
Check your email for the reset link.
Send a POST request to /reset-password with { "token": "received_token", "newPassword": "new_secure_password" }.

Conclusion
You’ve successfully implemented a secure password reset feature with MongoDB, Node.js, and Express. This approach ensures security by validating tokens, hashing passwords, and setting expiration times.

🚀 Next Steps? Implement front-end integration and improve security with rate limiting and better email templates!

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

nextjs tutorial video

Youtube Tutorial Series 📺

So you built a Next.js app, but you need a clear view of the entire operation flow to be able to identify performance bottlenecks before you launch. But how do you get started? Get the essentials on tracing for Next.js from @nikolovlazar in this video series 👀

Watch the Youtube series

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay