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

authwall

v2.0.0

Published

OTP-based authentication and full admin user-management REST API for Express + MongoDB

Downloads

603

Readme

🔐 authwall

OTP-based authentication + full admin user-management REST API for Express + MongoDB

npm version License: MIT Node.js PRs Welcome

Zero-friction auth for Express apps — OTP login, JWT tokens, RBAC, full admin API, audit logs, and more. Drop it in, configure, ship.


📋 Table of Contents


✨ Features

| | Feature | Details | |---|---|---| | 📧 | Email OTP login | Auto-registers new users on first login | | 🔐 | JWT authentication | Configurable expiry, no insecure defaults | | 👥 | RBAC | Role & permission system, fully configurable | | 🛡️ | Admin API | Suspend, ban, impersonate, bulk actions | | 📊 | Audit trail | Login history + admin logs with request IDs | | 📤 | CSV export | Download full user list | | 🚦 | Rate limiting | Built-in on OTP endpoints | | 🌐 | CORS | Configured out of the box | | ❤️ | Health check | GET /api/health | | 🎉 | Event system | Hook into every auth action via events | | 🧩 | Zero conflicts | All collections prefixed otpguard_* |


📦 Requirements

  • Node.js >= 18
  • Express 4+
  • MongoDB via Mongoose 8+

🚀 Installation

npm install authwall

⚡ Quick Start

// app.js
const express  = require('express');
const mongoose = require('mongoose');
const { authwall } = require('authwall');

const app = express();
app.use(express.json());

mongoose.connect(process.env.MONGO_URI || 'mongodb://localhost:27017/myapp');

authwall(app, {
  seed:        true,   // seed default roles & permissions on startup
  cleanupCron: true,   // auto-clean expired OTPs every 15 min
  config: {
    jwt: {
      secret: process.env.JWT_SECRET,   // ⚠️ required — set in your .env
    },
    email: {
      from: '[email protected]',
      transport: {
        host: process.env.MAIL_HOST,
        port: 587,
        auth: { user: process.env.MAIL_USER, pass: process.env.MAIL_PASS },
      },
    },
  },
});

app.listen(3000, () => console.log('Server running on port 3000'));

🧩 Using Your Own User Model

// models/User.js
const mongoose = require('mongoose');
const { addUserAdminFields } = require('authwall');

const userSchema = new mongoose.Schema({
  name:     String,
  email:    { type: String, unique: true },
  password: String,
}, { timestamps: true });

addUserAdminFields(userSchema); // adds status, roles, OTP helpers, etc.

module.exports = mongoose.model('User', userSchema);
// app.js
const User = require('./models/User');
authwall(app, { getUserModel: () => User, config: { ... } });

⚙️ Configuration

authwall(app, {
  config: {
    routePrefix:     '/api',   // default '/api'
    routeMiddleware: [],       // global middleware before all routes

    otp: {
      expiresInMinutes: 5,     // auto-set to 15 in development
      maxAttempts:      3,
      rateLimitCount:   3,
      rateLimitMinutes: 5,
      codeLength:       6,     // auto-set to 4 in development
    },

    jwt: {
      secret:    process.env.JWT_SECRET,  // required — no insecure default
      expiresIn: '7d',
    },

    defaultRoleSlug: 'user',
    adminRoleSlug:   'admin',
    autoRegister:    true,
    perPage:         15,

    rateLimit: {
      windowMs: 15 * 60 * 1000,  // 15 minutes
      max:      5,                // max OTP attempts per window
    },

    cors: {
      enabled:     true,
      origin:      process.env.CORS_ORIGIN || '*',
      credentials: true,
    },

    email: {
      from:      '[email protected]',
      transport: { /* nodemailer options */ },
    },
  },
});

📡 API Reference

All routes are mounted under routePrefix (default /api).

General

| Method | Endpoint | Description | |--------|----------|-------------| | GET | /api/health | Health check — returns service info |


Authentication

| Method | Endpoint | Auth | Description | |--------|----------|------|-------------| | POST | /api/auth/otp/send | — | Send OTP to email | | POST | /api/auth/otp/verify | — | Verify OTP, receive JWT | | POST | /api/auth/logout | Bearer | Logout | | GET | /api/auth/me | Bearer | Get current user |


Admin — Users

| Method | Endpoint | Description | |--------|----------|-------------| | GET | /api/admin/users | List users — filters: status, role, search, sort_by | | POST | /api/admin/users | Create user | | GET | /api/admin/users/:id | Get user | | PUT | /api/admin/users/:id | Update user | | DELETE | /api/admin/users/:id | Permanently delete user | | DELETE | /api/admin/users/:id/soft | Soft delete user | | GET | /api/admin/users/export | Download CSV |


Admin — User Actions

| Method | Endpoint | Description | |--------|----------|-------------| | POST | /api/admin/users/:id/suspend | Suspend user | | POST | /api/admin/users/:id/unsuspend | Unsuspend user | | POST | /api/admin/users/:id/temporary-ban | Temporary ban | | POST | /api/admin/users/:id/force-password-reset | Force password reset (sends email) | | POST | /api/admin/users/:id/remove-2fa | Remove 2FA | | POST | /api/admin/users/:id/terminate-sessions | Revoke all sessions | | GET | /api/admin/users/:id/login-history | Login history | | POST | /api/admin/users/:id/impersonate | Impersonate user | | POST | /api/admin/users/stop-impersonation | Stop impersonation |


Admin — Bulk Operations

| Method | Endpoint | Body | |--------|----------|------| | POST | /api/admin/users/bulk/suspend | { user_ids: [...] } | | POST | /api/admin/users/bulk/unsuspend | { user_ids: [...] } | | POST | /api/admin/users/bulk/assign-role | { user_ids: [...], role: "moderator" } | | POST | /api/admin/users/bulk/delete | { user_ids: [...] } |


Admin — Stats & Logs

| Method | Endpoint | Description | |--------|----------|-------------| | GET | /api/admin/statistics | User statistics | | GET | /api/admin/admin-logs | Admin audit log |


🔧 Middleware Helpers

const { makeAuthMiddleware, makeAdminMiddleware, makeRoleMiddleware } =
  require('authwall/src/middleware');

const auth  = makeAuthMiddleware(cfg, () => User);
const admin = makeAdminMiddleware(cfg);
const mod   = makeRoleMiddleware('moderator');

app.get('/dashboard',  auth, admin, handler);
app.get('/mod-panel',  auth, mod,   handler);

🔐 Two-Factor Authentication

When a user has 2FA enabled, POST /api/auth/otp/verify returns:

{
  "requires_2fa": true,
  "otp_id": "abc123",
  "message": "Two-factor authentication required."
}

The client then re-submits with a TOTP code or a backup recovery code:

// Option A — TOTP authenticator app
{ "otp_id": "abc123", "code": "381920", "totp_token": "654321" }

// Option B — backup recovery code (one-time use)
{ "otp_id": "abc123", "code": "381920", "backup_code": "ABCD1234" }

No separate /auth/2fa/recovery endpoint — backup codes work directly through the standard verify flow, keeping the API clean.


🎉 Event System

authwall emits events for every meaningful action. Subscribe to integrate with Slack, Datadog, webhooks, Socket.io, or any custom logic — without touching core code.

const { authwall, events, EVENTS } = require('authwall');

// Log every login
events.on(EVENTS.LOGIN_SUCCESS, (data) => {
  console.log(`✅ Login: ${data.email} from ${data.ip}`);
});

// Alert on failed 2FA attempts
events.on(EVENTS.TFA_FAILED, (data) => {
  slackAlert(`⚠️ Failed 2FA attempt for ${data.email} from ${data.ip}`);
});

// Trigger onboarding on new user
events.on(EVENTS.USER_CREATED, async (data) => {
  await crm.createContact({ email: data.email });
});

// Wildcard — catch everything
events.on('*', (payload) => {
  datadogClient.event({ title: payload.event, text: JSON.stringify(payload) });
});

Full Event List

| Event | Fired when | |-------|------------| | EVENTS.OTP_SENT | OTP email dispatched | | EVENTS.OTP_VERIFIED | OTP successfully verified | | EVENTS.OTP_FAILED | Invalid or rate-limited OTP attempt | | EVENTS.LOGIN_SUCCESS | User logged in | | EVENTS.LOGIN_FAILED | Failed login (suspended, not found, etc.) | | EVENTS.LOGOUT | User logged out | | EVENTS.USER_CREATED | New user registered or created by admin | | EVENTS.USER_UPDATED | User record updated | | EVENTS.USER_DELETED | User deleted | | EVENTS.USER_SUSPENDED | User account suspended | | EVENTS.USER_UNSUSPENDED | Suspension lifted | | EVENTS.TFA_ENABLED | 2FA activated | | EVENTS.TFA_DISABLED | 2FA disabled | | EVENTS.TFA_FAILED | Invalid TOTP or backup code | | EVENTS.DEVICE_TRUSTED | Device marked trusted | | EVENTS.DEVICE_REVOKED | Device or session revoked | | EVENTS.SESSIONS_TERMINATED | All sessions terminated | | EVENTS.TOKEN_REFRESHED | Refresh token rotated | | EVENTS.TOKEN_REUSE_DETECTED | Reuse attack detected — all family revoked | | EVENTS.ADMIN_ACTION | Any admin action | | EVENTS.IMPERSONATION_STARTED | Admin begins impersonation | | EVENTS.BULK_USERS_IMPORTED | CSV/JSON import complete | | EVENTS.BULK_USERS_SUSPENDED | Bulk suspend complete | | EVENTS.BULK_USERS_DELETED | Bulk delete complete | | EVENTS.BULK_ROLE_ASSIGNED | Bulk role assignment complete | | EVENTS.ACCOUNT_DELETION_REQUESTED | Grace-period deletion scheduled | | EVENTS.ACCOUNT_DELETION_COMPLETED | Account permanently purged | | EVENTS.EMAIL_SENT | Email dispatched | | EVENTS.EMAIL_FAILED | Email failed |


⚖️ Rate Limiting — Multi-Server Note

authwall uses in-memory rate limiting (zero extra dependencies). For multi-instance deployments behind a load balancer, swap to a Redis store:

npm install rate-limit-redis ioredis
const { rateLimit } = require('express-rate-limit');
const RedisStore    = require('rate-limit-redis');
const Redis         = require('ioredis');

const sharedLimiter = rateLimit({
  store:    new RedisStore({ client: new Redis(process.env.REDIS_URL) }),
  windowMs: 15 * 60 * 1000,
  max:      5,
});

🗄️ MongoDB Collections

| Collection | Description | |------------|-------------| | otpguard_roles | Roles | | otpguard_permissions | Permissions | | otpguard_otps | One-time passwords — TTL index, auto-deleted on expiry | | otpguard_login_histories | Login history | | otpguard_admin_logs | Admin audit trail with request IDs | | users | Users — your model or the built-in one |


🔒 Security Best Practices

  1. Always set JWT_SECRET in your environment — never hardcode it
  2. Use HTTPS in production
  3. Rate limiting is built-in on OTP endpoints — configure rateLimit for your needs
  4. Regularly rotate JWT secrets in high-security environments
  5. Monitor admin audit logs (GET /api/admin/admin-logs) for suspicious activity
  6. Admin accounts cannot modify/suspend/delete themselves — built-in protection
  7. Password reset tokens are never sent in API responses — always emailed to the user

📄 License

MIT © Susheel Kumar


💼 Open to Work

Susheel Kumar is available for freelance & full-time opportunities.

Built this package? He can build yours too.

📧 [email protected] — reach out anytime!