npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hiithere/otp-verification-module

v0.4.0

Published

Reusable Express OTP module with Redis storage, BullMQ delivery, validation, presets, and pluggable providers.

Readme

OTP Verification Module

Reusable Express OTP module for products that need OTP request, resend, and verify flows without rewriting the same backend code in every app.

It supports Redis active OTP storage, BullMQ notification jobs, Zod request validation, idempotency, rate limiting, WhatsApp delivery, metrics, queue dashboard wiring, and pluggable adapters.

Install

npm install @hiithere/otp-verification-module

Quick Start

import express from 'express';
import { createOtpModule } from '@hiithere/otp-verification-module';

const app = express();

app.use(express.json());

const otpModule = createOtpModule({
  preset: 'simple'
});

app.use('/otp', otpModule.router);

app.listen(3000);

process.on('SIGTERM', async () => {
  await otpModule.close();
  process.exit(0);
});

Routes exposed by the module:

POST /otp/request
POST /otp/resend
POST /otp/verify

Presets

Use presets when you want sane defaults without manually wiring every class.

simple

Best for smaller apps that still want Redis-backed OTP state and BullMQ delivery, but do not need enterprise audit/locking behavior.

const otpModule = createOtpModule({
  preset: 'simple'
});

Simple preset uses:

RedisOtpRepository
RedisRateLimiter
RedisIdempotencyStore
BullMqNotificationQueue
NoOpDistributedLock
NoOpCooldownStore
NoOpAuditLogger

enterprise

Default preset. Best when multiple app instances may process OTP requests and you want stronger operational controls.

const otpModule = createOtpModule({
  preset: 'enterprise'
});

Enterprise preset uses:

RedisOtpRepository
RedisRateLimiter
RedisIdempotencyStore
BullMqNotificationQueue
RedisDistributedLock
RedisCooldownStore
MongoAuditLogger

Configuration Object

Use config when the consuming app needs different Redis, queue, rate limit, cooldown, or OTP policy behavior.

const otpModule = createOtpModule({
  preset: 'simple',
  config: {
    redis: {
      url: process.env.REDIS_URL
    },
    queue: {
      name: 'otp-notifications'
    },
    rateLimit: {
      limit: 3,
      windowSeconds: 60
    },
    idempotency: {
      ttlSeconds: 300
    },
    cooldown: {
      ttlSeconds: 30
    },
    otp: {
      defaultPolicy: {
        length: 6,
        ttlSeconds: 300,
        maxAttempts: 3
      },
      purposePolicies: {
        LOGIN: {
          ttlSeconds: 120
        },
        CHECKOUT: {
          ttlSeconds: 60,
          maxAttempts: 5
        },
        PASSWORD_RESET: {
          ttlSeconds: 600,
          maxAttempts: 3
        }
      }
    }
  }
});

Shortcuts are also supported:

const otpModule = createOtpModule({
  preset: 'simple',
  redisUrl: process.env.REDIS_URL,
  queueName: 'otp-notifications',
  otpPolicies: {
    LOGIN: {
      ttlSeconds: 120,
      maxAttempts: 3
    }
  }
});

Policy values are merged with safe defaults, so a purpose can override only what it needs.

Worker

The API only queues the notification job. A worker must run separately to deliver OTP messages.

import {
  createOtpNotificationWorker,
  ClientNotificationProvider,
  NotificationDispatcher,
  OTP_CHANNEL,
  WhatsappCloudProvider
} from '@hiithere/otp-verification-module';

createOtpNotificationWorker({
  notificationProvider: new NotificationDispatcher({
    providers: {
      [OTP_CHANNEL.WHATSAPP]: new WhatsappCloudProvider({
        accessToken: process.env.WHATSAPP_ACCESS_TOKEN,
        phoneNumberId: process.env.WHATSAPP_PHONE_NUMBER_ID,
        templateName: process.env.WHATSAPP_TEMPLATE_NAME,
        templateLanguage: process.env.WHATSAPP_TEMPLATE_LANGUAGE,
        graphApiVersion: process.env.WHATSAPP_GRAPH_API_VERSION
      })
    }
  })
});

Client App Delivery

Use ClientNotificationProvider when the product using this package wants to deliver OTP through its own code. This is useful for in-app notifications, custom SMS vendors, internal queues, Firebase, sockets, or any delivery system owned by the consuming app.

import {
  createClientOtpNotificationWorker
} from '@hiithere/otp-verification-module';

createClientOtpNotificationWorker({
  providerName: 'my-product-in-app',
  sendOtp: async ({ userId, recipient, otp, purpose, requestId }) => {
    await myInAppNotificationService.send({
      userId,
      recipient,
      title: 'Your verification code',
      body: `Your ${purpose} OTP is ${otp}`,
      metadata: {
        requestId
      }
    });

    return {
      provider: 'my-product-in-app',
      status: 'SENT'
    };
  }
});

Then request OTP with:

{
  "userId": "user_1",
  "purpose": "LOGIN",
  "channel": "IN_APP",
  "recipient": "user_1"
}

The service still owns OTP generation, hashing, storage, rate limiting, and verification. The client provider owns only delivery.

Consumer Integration Chain

Most apps need two small files.

API server:

import express from 'express';
import { createOtpModule } from '@hiithere/otp-verification-module';

const app = express();
const otpModule = createOtpModule({ preset: 'simple' });

app.use(express.json());
app.use('/otp', otpModule.router);

app.listen(3000);

Worker:

import { createClientOtpNotificationWorker } from '@hiithere/otp-verification-module';

createClientOtpNotificationWorker({
  sendOtp: async ({ userId, otp }) => {
    await yourDeliveryService.sendOtp(userId, otp);
  }
});

Frontend:

POST /otp/request -> show OTP input
POST /otp/verify -> if OTP_VERIFIED, create session/JWT in your app
POST /otp/resend -> retry delivery after cooldown policy allows it

The package does not create users, sessions, JWTs, roles, or permissions. Your app owns those business decisions after OTP verification succeeds.

Request Examples

Request OTP:

curl -X POST http://localhost:3000/otp/request \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: login-user-1-001" \
  -d '{
    "userId": "user_1",
    "purpose": "LOGIN",
    "channel": "WHATSAPP",
    "recipient": "+919999999999"
  }'

Verify OTP:

curl -X POST http://localhost:3000/otp/verify \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "user_1",
    "purpose": "LOGIN",
    "otp": "123456"
  }'

Environment

NODE_ENV=development
PORT=4002
API_PREFIX=/api/v1
MONGO_URI=mongodb://127.0.0.1:27017/otp_service
REDIS_URL=redis://127.0.0.1:6379
WHATSAPP_ACCESS_TOKEN=
WHATSAPP_PHONE_NUMBER_ID=
WHATSAPP_TEMPLATE_NAME=otp_login
WHATSAPP_TEMPLATE_LANGUAGE=en_US
WHATSAPP_GRAPH_API_VERSION=v20.0

Redis is required for the default simple and enterprise presets. MongoDB is required only when using the default enterprise audit logger.

Adapter Overrides

Every main dependency can be replaced. This is the main plugin-style design point.

const otpModule = createOtpModule({
  preset: 'simple',
  otpRepository,
  notificationQueue,
  rateLimiter,
  idempotencyStore,
  auditLogger,
  distributedLock,
  cooldownStore,
  otpPolicyService
});

This lets one product use the default Redis/BullMQ setup while another product injects custom storage, custom SMS/WhatsApp providers, or no-op infrastructure.

Local Development

npm install
npm run dev
npm run worker:notifications

Useful local routes from the included app:

GET /health
GET /metrics
GET /admin/queues
POST /api/v1/otp/request
POST /api/v1/otp/resend
POST /api/v1/otp/verify

Publish Checklist

Before publishing:

npm run lint
npm test
npm run pack:dry-run
npm publish

The published package name is @hiithere/otp-verification-module.

Security Notes

OTP values are hashed before active storage. Raw OTP values are only passed to the notification queue/provider for delivery.

Do not expose Redis, MongoDB, BullMQ dashboard, or WhatsApp credentials publicly. Protect /admin/queues with authentication before using it in production.

Use HTTPS in production and keep idempotency keys unique per logical request.

Architecture

Route
  -> Zod validation middleware
  -> OtpController
  -> OtpService
      -> OtpPolicyService
      -> OtpTokenService
      -> OtpRepository
      -> RateLimiter
      -> IdempotencyStore
      -> DistributedLock
      -> CooldownStore
      -> NotificationQueue
      -> AuditLogger

The code follows dependency injection and adapter-style interfaces so the OTP domain logic stays stable while infrastructure can change per product.