How to create OTP flow system on Node.js (step-by- step)

Published: (January 16, 2026 at 08:56 AM EST)
3 min read
Source: Dev.to

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

StepWhat it Does
OTP GenerationCreates a secure, random numeric code
SetterBinds OTP to an identifier (email/phone)
SenderDelivers code via email/SMS
VerifierChecks submitted code against stored one
CooldownPrevents 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!

Back to Blog

Related posts

Read more »

NodeJS 101 — Part 2 MySQL

🚀 การสร้าง API โดยใช้ JavaScript Node.js Express คู่มือการพัฒนา RESTful API แบบครบวงจรด้วย Node.js, Express, Sequelize และ MySQL ! https://media2.dev.to/dynam...