In today's digital world, security is paramount, especially when it comes to user authentication. One-time passwords (OTPs) are widely used to verify user identities during online transactions, account logins, and more. In this blog, we'll explore how to implement an OTP verification system using Node.js, Express.js, and MongoDB.
Understanding the Components
Before diving into the implementation details, let's understand the various components of our OTP verification system:
Express.js: A fast, unopinionated, minimalist web framework for Node.js. We'll use Express.js to handle HTTP requests and responses.
MongoDB: A popular NoSQL database used for storing and retrieving OTP data.
Nodemailer: A module for Node.js applications that allows easy email sending.
Randomstring: A library to generate random strings, which we'll use to create OTPs.
Project Structure Overview
otp-verification-system/
│
├── controllers/
│ └── otpController.js
│
├── models/
│ └── otpModel.js
│
├── routes/
│ └── otpRoutes.js
│
├── utils/
│ └── sendEmails.js
│
├── index.js
│
├── .env
│
└── package.json
Understanding the Project Structure
controllers/: This directory contains the controller functions responsible for handling the business logic of our OTP verification system. In this case,
otpController.js
holds the logic for sending and verifying OTPs.models/: Here, we define the data models for our MongoDB database. The
otpModel.js
file specifies the schema for storing OTPs.routes/: This directory houses the route definitions for our Express.js application. The
otpRoutes.js
file specifies the endpoints for sending and verifying OTPs.utils/: This directory contains utility functions used within our application. The
sendEmails.js
file handles email sending using Nodemailer.index.js: This is the entry point of our application where we set up our Express server, configure middleware, connect to the MongoDB database, and define route handling.
.env: This file stores environment variables such as database credentials, SMTP configuration, and any other sensitive information. We use the
dotenv
package to load these variables into our application.package.json: This file contains metadata about our project and lists the dependencies required for our application. It also includes scripts for running and testing our application.
Environment Variables in .env
PORT=3000
MONGODB_USER=username
MONGODB_PASSWORD=password
SMPT_HOST=smtp.example.com
SMPT_PORT=587
SMPT_MAIL=your_email@example.com
SMPT_APP_PASS=your_smtp_password
Setting Up the Environment
First, let's set up our development environment. Make sure you have Node.js and npm installed on your system. Then, create a new directory for your project and initialize it with npm:
mkdir otp-generator
cd otp-generator
npm init -y
Now, install the required dependencies:
npm install express cors body-parser mongoose nodemailer randomstring dotenv
Implementing the OTP Verification System
Now, let's implement the OTP verification system step by step.
Database Setup: We'll start by setting up our MongoDB database and defining the OTP schema.
// models/otpModel.js
const mongoose = require('mongoose');
const otpSchema = new mongoose.Schema({
email: { type: String, required: true },
otp: { type: String, required: true },
});
const Otps = mongoose.model('otps', otpSchema);
module.exports = Otps;
Creating Routes: Next, we'll define the routes for sending and verifying OTPs.
// routes/otpRoutes.js
const express = require('express');
const { sendOTP, verifyOTP } = require('../controllers/otpController');
const router = express.Router();
router.get('/sendOTP', sendOTP);
router.get('/verifyOTP', verifyOTP);
module.exports = router;
Implementing Controller Logic: Now, let's implement the logic for sending and verifying OTPs.
// controllers/otpController.js
const Otps = require('../models/otpModel.js');
const randomstring = require('randomstring');
const sendEmail = require('../utils/sendEmails');
// Generate OTP
function generateOTP() {
return randomstring.generate({
length: 6,
charset: 'numeric'
});
}
// Send OTP to the provided email
exports.sendOTP = async (req, res, next) => {
try {
const { email } = req.query;
const otp = generateOTP(); // Generate a 6-digit OTP
const newOTP = new Otps({ email, otp });
await newOTP.save();
// Send OTP via email
await sendEmail({
to: email,
subject: 'Your OTP',
message: `<p>Your OTP is: <strong>${otp}</strong></p>`,
});
res.status(200).json({ success: true, message: 'OTP sent successfully' });
} catch (error) {
console.error('Error sending OTP:', error);
res.status(500).json({ success: false, error: 'Internal server error' });
}
};
// Verify OTP provided by the user
exports.verifyOTP = async (req, res, next) => {
try {
const { email, otp } = req.query;
const existingOTP = await Otps.findOneAndDelete({ email, otp });
if (existingOTP) {
// OTP is valid
res.status(200).json({ success: true, message: 'OTP verification successful' });
} else {
// OTP is invalid
res.status(400).json({ success: false, error: 'Invalid OTP' });
}
} catch (error) {
console.error('Error verifying OTP:', error);
res.status(500).json({ success: false, error: 'Internal server error' });
}
};
Configuring Email Sending: Finally, we'll configure Nodemailer to send emails containing the OTP.
// utils/sendEmails.js
const nodeMailer = require("nodemailer");
const sendEmail = async (options) => {
const transporter = nodeMailer.createTransport({
host: process.env.SMPT_HOST,
port: process.env.SMPT_PORT,
secure: true, // Use SSL
auth: {
user: process.env.SMPT_MAIL,
pass: process.env.SMPT_APP_PASS,
},
authMethod: 'LOGIN', // Specify the authentication method
});
const mailOptions = {
from: process.env.SMPT_MAIL,
to: options.to,
subject: options.subject,
html: options.message,
};
await transporter.sendMail(mailOptions);
};
module.exports = sendEmail;
Setting Up Express Server: Finally, we'll set up our Express server to listen to incoming requests.
// index.js
const express = require("express");
const app = express();
const PORT = process.env.PORT || 3000;
const cors = require("cors");
const bodyParser = require("body-parser");
const mongoose = require('mongoose');
const otpRoutes = require('./routes/otpRoutes');
require("dotenv").config();
const dbUser = process.env.MONGODB_USER;
const dbPassword = process.env.MONGODB_PASSWORD;
mongoose
.connect(
`mongodb+srv://${dbUser}:${dbPassword}@cluster0.re3ha3x.mongodb.net/otp`,
{ useNewUrlParser: true, useUnifiedTopology: true } // Add these options for MongoDB connection
)
.then(() => {
console.log("Connected to MongoDB database!");
})
.catch((error) => {
console.error("Connection failed!", error); // Log the error for better debugging
});
app.use(
cors({
origin: "*",
})
);
app.use(bodyParser.json());
app.use(express.static("public"));
app.use(express.json());
app.use('/api', otpRoutes);
app.listen(PORT, () => {
console.log(`Server started on port ${PORT}`);
});
Conclusion
Congratulations! You've successfully implemented an OTP verification system using Node.js, Express.js, and MongoDB. This system enhances the security of your application by adding an extra layer of authentication. Feel free to customize and extend this system to meet the specific requirements of your project.
Exploring the Code
Visit the GitHub repository to explore the code in detail.
Feel free to customize the blog according to your preferences and provide more details or explanations where needed.