How to create OTP flow system on Node.js (step-by- step)
Source: Dev.to
🚀 What You’ll Build
- Endpoint to request/send OTP
- Endpoint to verify OTP
- Basic user model (memory/DB)
- Configurable sender (Email/SMS)
📦 Step 1 — Install & Set Up
Create a new Node.js project and install the required dependencies:
mkdir auth-verify-otp
cd auth-verify-otp
npm init -y
npm install express auth-verify dotenv
Create a .env file to store sensitive configuration:
PORT=3000
EMAIL_HOST=smtp.gmail.com
EMAIL_USER=me@example.com
EMAIL_PASS=yourEmailPassword
EMAIL_PORT=yourEmailPort
Tip: For Gmail, generate an App Password (recommended instead of using your real password).
📧 Step 2 — Configure OTP Sender
In index.js set up auth-verify and configure an email sender. The OTP manager automatically handles generation, storage, cooldowns, expiration, and more.
require('dotenv').config();
const express = require('express');
const AuthVerify = require('auth-verify');
const app = express();
app.use(express.json());
// Initialize auth-verify
const auth = new AuthVerify();
// Configure email sender
auth.otp.sender({
via: 'email',
host: process.env.EMAIL_HOST,
sender: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
port: process.env.EMAIL_PORT
});
📮 Step 3 — OTP Request Route
When a user hits /send-otp, generate an OTP and send it by email.
app.post('/send-otp', async (req, res) => {
try {
const { email } = req.body;
await auth.otp.send(email, {
otpLen: 5, // length of OTP code
subject: "Account verification",
text: `Your OTP code is ${auth.otp.code}`
});
return res.json({ message: 'OTP sent!' });
} catch (err) {
console.error(err);
return res.status(500).json({ error: 'Failed to send OTP' });
}
});
auth.otp.code contains the generated code that is sent to the user.
🔍 Step 4 — OTP Verification
Create a route to verify the OTP entered by the user.
app.post('/verify-otp', async (req, res) => {
try {
const { email, code } = req.body;
const isValid = await auth.otp.verify(email, code);
if (!isValid) {
return res.status(400).json({ verified: false, message: 'Invalid or expired OTP' });
}
return res.json({ verified: true, message: 'OTP verified successfully!' });
} catch (err) {
return res.status(500).json({ error: 'Verification error' });
}
});
auth.otp.verify() returns true or false, allowing you to branch your logic accordingly.
🔄 Step 5 — Optional: Cooldown & Resend Logic
To prevent spam and brute‑forcing, auth-verify supports cooldowns. After sending an OTP, the user must wait before requesting another one.
auth.otp.cooldown('30s'); // cooldown before OTP can be resent
auth.otp.resend('user@example.com');
The cooldown logic protects both user experience and security.
🧠 What Happened Behind the Scenes
| Step | What it Does |
|---|---|
| OTP Generation | Creates a secure, random numeric code |
| Setter | Binds OTP to an identifier (email/phone) |
| Sender | Delivers code via email/SMS |
| Verifier | Checks submitted code against stored one |
| Cooldown | Prevents spam & brute‑force attacks |
All of this heavy lifting is built into auth-verify, so you don’t need to manually generate tokens, manage expiration, or implement cooldown logic.
🏁 Final Thoughts
Building an OTP system from scratch involves several moving parts—generating tokens, sending them securely, storing/expiring codes, and verifying them. Libraries like auth-verify simplify the process, letting you focus on product logic rather than boilerplate.
Now your signup and login flows can support secure OTP validation with just a handful of routes!