- vừa được xem lúc

Blog#220: 🔐Implementing Secure Password Reset Functionality in Node.js Express

0 0 28

Người đăng: NGUYỄN ANH TUẤN

Theo Viblo Asia

220

Hi, I'm Tuan, a Full-stack Web Developer from Tokyo 😊. Follow my blog to not miss out on useful and interesting articles in the future.

1. Introduction

In this article, we will discuss how to implement secure password reset functionality in a Node.js Express application. Password reset is a critical feature to ensure security and a good user experience. To achieve this, we'll walk through the process step by step, from setting up the environment to sending a reset email and ultimately updating the user's password.

2. Setting Up the Project

2.1 Initialize the Project

First, create a new folder for your project and navigate into it using the command line. Then, run the following command to initialize the project with a package.json file:

npm init -y

2.2 Install Dependencies

Next, install the required dependencies for this project by running the following command:

npm install express mongoose bcryptjs jsonwebtoken nodemailer dotenv

These dependencies include:

  • express: The core Express framework
  • mongoose: A MongoDB object modeling library for Node.js
  • bcryptjs: A library for hashing and comparing passwords
  • jsonwebtoken: A library for generating and verifying JSON Web Tokens
  • nodemailer: A module for sending emails
  • dotenv: A module for loading environment variables from a .env file

3. Setting Up the Environment

3.1 Create a .env File

Create a .env file in the project root to store sensitive data and environment-specific configuration. Add the following lines to the file:

MONGODB_URI=mongodb://localhost:27017/password-reset
EMAIL_SERVICE=your_email_service
EMAIL_USER=your_email_address
EMAIL_PASS=your_email_password
JWT_SECRET=your_jwt_secret

Replace the placeholders with the appropriate values for your email service and account credentials.

3.2 Load Environment Variables

In the main application file (e.g., app.js), import and configure the dotenv module to load environment variables from the .env file:

require('dotenv').config();

4. Setting Up the Database

4.1 Connect to MongoDB

Use Mongoose to establish a connection to the MongoDB database. Update your app.js file with the following code:

const express = require('express');
const mongoose = require('mongoose'); const app = express();
app.use(express.json()); mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log('Connected to MongoDB')) .catch((err) => console.error('Failed to connect to MongoDB:', err)); const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server is running on port ${PORT}`));

4.2 Define the User Schema and Model

Create a new folder named models and inside it, create a file named User.js. Define the User schema and model with the following code:

const mongoose = require('mongoose');
const bcrypt = require('bcryptjs'); const userSchema = new mongoose.Schema({ email: { type: String, required: true, unique: true }, password: { type: String, required: true }, passwordResetToken: { type: String }, passwordResetExpires: { type: Date },
}); // Hash the password before saving it to the database
userSchema.pre('save', async function (next) { if (!this.isModified('password')) return next(); const salt = await bcrypt.genSalt(10); this.password = await bcrypt.hash(this.password, salt); next();
}); // Instance method to validate the user's password
userSchema.methods.validatePassword = async function (password) { return await bcrypt.compare(password, this.password);
}; const User = mongoose.model('User', userSchema);
module.exports = User;

5. Implementing Password Reset Functionality

5.1 Set Up the Routes

Create a new folder named routes and inside it, create a file named auth.js. Set up the following routes:

  • /auth/forgot-password: To initiate the password reset process
  • /auth/reset-password: To handle the actual password reset
const express = require('express');
const router = express.Router();
const { forgotPassword, resetPassword } = require('../controllers/authController'); router.post('/forgot-password', forgotPassword);
router.post('/reset-password', resetPassword); module.exports = router;

Now, import and use the auth.js router in the app.js file:

const authRoutes = require('./routes/auth'); app.use('/auth', authRoutes);

5.2 Create the Auth Controller

Create a new folder named controllers and inside it, create a file named authController.js. This file will contain the controller functions for handling the password reset process.

const User = require('../models/User');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const nodemailer = require('nodemailer'); const transporter = nodemailer.createTransport({ service: process.env.EMAIL_SERVICE, auth: { user: process.env.EMAIL_USER, pass: process.env.EMAIL_PASS, },
}); async function forgotPassword(req, res) { // TODO: Implement the forgotPassword function
} async function resetPassword(req, res) { // TODO: Implement the resetPassword function
} module.exports = { forgotPassword, resetPassword,
};

5.3 Implement the forgotPassword Function

In the authController.js file, implement the forgotPassword function to handle the password reset request.

async function forgotPassword(req, res) { // Find the user by email const user = await User.findOne({ email: req.body.email }); if (!user) { return res.status(404).json({ error: 'User not found' }); } // Generate a password reset token and set its expiration date const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' }); user.passwordResetToken = token; user.passwordResetExpires = Date.now() + 3600000; // 1 hour await user.save(); // Send the password reset email const resetUrl = `http://${req.headers.host}/auth/reset-password?token=${token}`; const mailOptions = { from: process.env.EMAIL_USER, to: user.email, subject: 'Password Reset Request', html: ` <p>You requested a password reset. Click the link below to reset your password:</p> <a href="${resetUrl}">${resetUrl}</a> `, }; try { await transporter.sendMail(mailOptions); res.json({ message: 'Password reset email sent' }); } catch (err) { console.error('Failed to send password reset email:', err); res.status(500).json({ error: 'Failed to send password reset email' }); }
}

5.4 Implement the resetPassword Function

In the authController.js file, implement the resetPassword function to handle the password reset confirmation and update the user's password.

async function resetPassword(req, res) { // Validate the password reset token const token = req.query.token; const decodedToken = jwt.verify(token, process.env.JWT_SECRET); // Find the user by their ID and token, and check if the token is still valid const user = await User.findOne({ _id: decodedToken.id, passwordResetToken: token, passwordResetExpires: { $gt: Date.now() }, }); if (!user) { return res.status(401).json({ error: 'Invalid or expired password reset token' }); } // Update the user's password and remove the reset token and its expiration date user.password = req.body.password; user.passwordResetToken = undefined; user.passwordResetExpires = undefined; await user.save(); // Send a confirmation email const mailOptions = { from: process.env.EMAIL_USER, to: user.email, subject: 'Password Reset Confirmation', html: ` <p>Your password has been successfully reset. If you did not initiate this request, please contact us immediately.</p> `, }; try { await transporter.sendMail(mailOptions); res.json({ message: 'Password reset successful' }); } catch (err) { console.error('Failed to send password reset confirmation email:', err); res.status(500).json({ error: 'Failed to send password reset confirmation email' }); }
}

6. Testing the Implementation

To test the password reset functionality, you can use tools like Postman or curl to send HTTP requests to the /auth/forgot-password and /auth/reset-password routes.

  • Send a POST request to/auth/forgot-password with a valid email address in the request body.
  • Check your email inbox for the password reset email and click the reset link.
  • Send a POST request to /auth/reset-password with the token from the reset link and the new password in the request body.
  • Check your email inbox for the password reset confirmation email.

Conclusion

In this article, we have successfully implemented secure password reset functionality in a Node.js Express application. We have walked through the entire process, from setting up the environment and database to handling password reset requests and updating the user's password.

By following these steps, you can ensure that your application provides a secure and user-friendly password reset experience. Always remember to use up-to-date libraries and follow best practices when handling sensitive data, such as passwords and tokens.

And Finally

As always, I hope you enjoyed this article and got something new. Thank you and see you in the next articles!

If you liked this article, please give me a like and subscribe to support me. Thank you. 😊

Ref

Bình luận

Bài viết tương tự

- vừa được xem lúc

Cách mình "hack" được vào hẹ thống của SMAS để xem điểm.

Cách mà mình "hack" được vào hệ thống của SMAS. Thật ra dùng từ hack cũng không đúng lắm, chỉ là một vài trick để lừa hệ thống mà thôi.

0 0 146

- vừa được xem lúc

[NodeJs] Tạo QR Code trong nodeJs với qrcode

Tạo mã QR Code trong nodejs với qrcode. QR Code là gì. Tạo QR code với qrcode. Cài đặt thư viện qrcode.

0 0 34

- vừa được xem lúc

Áp dụng kiến trúc 3 Layer Architecture vào project NodeJS

The problem encountered. Các framework nodejs phổ biết như Express cho phép chúng ta dễ dàng tạo ra Resful API xử lí các request từ phía client một cách nhanh chóng và linh hoạt.

0 0 80

- vừa được xem lúc

Router, Controller trong Express

Mở đầu. Xin chào các bạn mình đã quay trở lại rồi đây, tiếp tục với series Nodejs cơ bản thì hôm nay mình sẽ giới thiệu đến các bạn Express Router và Controller.

0 0 43

- vừa được xem lúc

Xây dựng CRUD RESTful API sử dụng Node, Express, MongoDB.

Introduction. Trong phạm vi bài viết này chúng ta sẽ cùng tìm hiểu về cách tạo restful api với Node, Express và MongoDB. . Xử lý các hoạt động crud.

0 0 226

- vừa được xem lúc

Rate time limit trong NodeJS

Chào các bạn, lại là mình đây. Hôm nay mình xin giới thiệu tới các bạn một kỹ thuật rất hay ho và hữu ích đó là Rate Limiting. 1. Rate Limiting là gì.

0 0 64