DEV Community

Cover image for How to create OTP flow system on Node.js (step-by- step)
Jahongir Sobirov
Jahongir Sobirov

Posted on

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

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

🧠 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
});
Enter fullscreen mode Exit fullscreen mode

📮 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' });
  }
});
Enter fullscreen mode Exit fullscreen mode

auth.otp.code is 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' });
  }
});
Enter fullscreen mode Exit fullscreen mode

verify() returns true/false so 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")
Enter fullscreen mode Exit fullscreen mode

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)