One-Time Passwords (OTP) are everywhere — login verification, sign-up confirmation, 2FA, password resets, etc. In this tutorial, we’ll build a simple and secure OTP authentication flow in Node.js using the auth-verify library.
The auth-verify package helps you generate, send, and verify OTP codes quickly, and supports multiple delivery methods like email, SMS, Telegram, etc. It also includes useful features like cooldowns and JWT integration.
🚀 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
First create a new Node.js project and install 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 config:
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 real password).
📧 Step 2 — Configure OTP Sender
Inside index.js set up auth-verify and configure a sender (e.g., email). 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.codeis generated code for showing to user
🔍 Step 4 — OTP Verification
Create a route to verify the OTP the user enters:
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' });
}
});
verify()returnstrue/falseso you can easily branch logic.
🔄 Step 5 — Optional: Cooldown & Resend Logic
To prevent spam and brute-forcing, auth-verify supports cooldowns: after sending an OTP, the user must wait a bit before requesting again.
auth.otp.cooldown("30s"); // cooldown before OTP can be resent
auth.otp.resend("user@example.com")
The cooldown logic prevents rapid repeated OTP sends (important for both UX 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 |
All this heavy lifting comes built-in with auth-verify, so you don’t have to manually generate tokens, manage expiration, or build cooldown logic yourself.
🏁 Final Thoughts
Building an OTP system from scratch involves a few moving parts — generating tokens, sending them securely, storing/expiring codes, and verifying them. Libraries like auth-verify simplify this process, letting you focus on product logic rather than boilerplate.
Now your signup and login flows can support secure OTP validation in just a handful of routes!
Top comments (0)