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

@amine_boutouil/bouncer

v1.0.0

Published

Production-grade security middleware for Express.js - audit logging, RBAC, OTP/2FA, and rate limiting

Downloads

100

Readme

@amine_boutouil/bouncer

🛡️ Production-grade security middleware for Express.js — audit logging, RBAC, OTP/2FA, and rate limiting.

TypeScript Node.js License: MIT

A reusable, hardened security middleware package designed for high-security and compliance-sensitive Express.js environments.


Features

| Module | Capabilities | | ----------------- | -------------------------------------------------------------------------------------------------------- | | Audit Logging | SHA256 hash-chained logs, HMAC signing, tamper detection, pluggable storage, log rotation | | RBAC Engine | JWT verification (HS256/RS256), role hierarchy (GOD > ADMIN > MODERATOR > USER), permission-based access | | OTP / 2FA | TOTP generation & verification, QR codes, recovery codes, replay attack protection | | Rate Limiting | Configurable rate limits, anti-bruteforce guards, request fingerprinting | | Core | Zod config validation, centralized error handling, structured Winston logging, full TypeScript types |


Installation

npm install @amine_boutouil/bouncer

Peer Dependencies

npm install express

Quick Start

import express from "express";
import {
  createAuditMiddleware,
  createRBACMiddleware,
  createRateLimitMiddleware,
  createBruteforceGuard,
  create2FAManager,
  bouncerErrorHandler,
  MemoryStorageAdapter,
} from "@amine_boutouil/bouncer";

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

// ── Rate Limiting (global) ──────────────────────────────
const rateLimit = createRateLimitMiddleware({
  points: 100,
  duration: 60,
});
app.use(rateLimit);

// ── Audit Logging ───────────────────────────────────────
const audit = createAuditMiddleware({
  storage: new MemoryStorageAdapter(), // Use FileStorageAdapter in production
  hmacEnabled: true,
  hmacSecret: process.env.HMAC_SECRET!, // min 32 chars
});
app.use(audit);

// ── RBAC ────────────────────────────────────────────────
const rbac = createRBACMiddleware({
  secret: process.env.JWT_SECRET!,
  algorithm: "HS256",
  issuer: "my-app",
  audience: "my-api",
});

// ── Protected Route ─────────────────────────────────────
app.get("/admin", rbac.authenticate, rbac.requireRole("ADMIN"), (req, res) => {
  res.json({ message: `Hello ${req.user!.userId}` });
});

// ── Error Handler (must be last) ────────────────────────
app.use(bouncerErrorHandler);

app.listen(3000);

Modules

1. Audit Logging

Intercepts state-changing HTTP methods (POST, PUT, PATCH, DELETE) and produces structured, hash-chained audit logs.

import {
  createAuditMiddleware,
  verifyAuditIntegrity,
  FileStorageAdapter,
} from "@amine_boutouil/bouncer";

// File-based storage with rotation
const storage = new FileStorageAdapter({
  filePath: "./logs/audit.log",
  maxFileSize: 10 * 1024 * 1024, // 10MB
  maxFiles: 5,
});

const audit = createAuditMiddleware({
  storage,
  hmacEnabled: true,
  hmacSecret: process.env.HMAC_SECRET!,
  sensitiveFields: ["password", "token", "secret", "creditCard", "otp"],
  methods: ["POST", "PUT", "PATCH", "DELETE"],
});

app.use(audit);

// Verify log integrity
const result = await verifyAuditIntegrity(
  "./logs/audit.log",
  process.env.HMAC_SECRET,
);
if (!result.valid) {
  console.error(
    `Tamper detected at entry ${result.brokenAt}: ${result.reason}`,
  );
}

Audit entry structure:

{
  "id": "uuid-v4",
  "timestamp": "2026-01-01T00:00:00.000Z",
  "userId": "user-123",
  "role": "ADMIN",
  "ip": "192.168.1.1",
  "method": "POST",
  "route": "/api/users",
  "body": { "username": "john", "password": "[REDACTED]" },
  "statusCode": 201,
  "previousHash": "sha256-of-previous-entry",
  "hash": "sha256-of-this-entry",
  "hmac": "hmac-signature"
}

Custom storage adapter:

import { AuditStorageAdapter, AuditEntry } from "@amine_boutouil/bouncer";

class DatabaseAdapter implements AuditStorageAdapter {
  async append(entry: AuditEntry): Promise<void> {
    /* INSERT into DB */
  }
  async readAll(): Promise<AuditEntry[]> {
    /* SELECT * */
  }
  async getLastEntry(): Promise<AuditEntry | null> {
    /* SELECT ... ORDER BY ... LIMIT 1 */
  }
  async close(): Promise<void> {
    /* cleanup */
  }
}

2. RBAC Engine

JWT-based authentication and role/permission-based authorization.

import { createRBACMiddleware, DefaultRole } from "@amine_boutouil/bouncer";

const rbac = createRBACMiddleware({
  secret: process.env.JWT_SECRET!,
  algorithm: "HS256", // or 'RS256' with public key
  audience: "my-api",
  issuer: "my-auth-server",
  // Custom hierarchy (optional)
  roleHierarchy: {
    GOD: 100,
    ADMIN: 75,
    MODERATOR: 50,
    USER: 25,
  },
});

// Authenticate (verifies JWT, sets req.user)
app.use("/api", rbac.authenticate);

// Require minimum role
app.get("/admin", rbac.requireRole("ADMIN"), handler);

// GOD mode only
app.delete("/system/reset", rbac.requireGodMode(), handler);

// Permission-based
app.post("/articles", rbac.requirePermission("articles:write"), handler);

Expected JWT payload:

{
  "userId": "user-123",
  "role": "ADMIN",
  "permissions": ["read", "write", "delete"],
  "aud": "my-api",
  "iss": "my-auth-server",
  "exp": 1735689600
}

3. OTP / 2FA

Full TOTP-based two-factor authentication with recovery codes.

import { create2FAManager, TwoFactorUser } from "@amine_boutouil/bouncer";

const twoFactor = create2FAManager({
  issuer: "MyApp",
  step: 30,
  window: 1,
  recoveryCodeCount: 10,
  recoveryCodeLength: 16,
});

// Enable 2FA for a user
const result = await twoFactor.enable2FA(user);
// result.secret — store in DB
// result.qrCodeDataURL — show to user
// result.recoveryCodes — show ONCE, user must save

// Verify TOTP token (with replay protection)
const isValid = twoFactor.verify2FA(user, "123456");

// Use recovery code (one-time)
const recovered = twoFactor.verifyAndConsumeRecoveryCode(
  user,
  "ABCD-1234-EFGH-5678",
);

// Disable 2FA
twoFactor.disable2FA(user);

Security features:

  • Replay attack protection (hashes last successful OTP)
  • Recovery codes are SHA256 hashed before storage
  • Cryptographically secure random generation via Node crypto
  • Configurable time window tolerance

4. Rate Limiting

import {
  createRateLimitMiddleware,
  createBruteforceGuard,
} from "@amine_boutouil/bouncer";

// Global rate limit
app.use(
  createRateLimitMiddleware({
    points: 100, // requests
    duration: 60, // per 60 seconds
    blockDuration: 0,
    message: "Rate limit exceeded",
  }),
);

// Anti-bruteforce for login
app.post(
  "/login",
  createBruteforceGuard({
    maxAttempts: 5,
    blockDuration: 900, // 15 min block
    freeRetries: 2,
  }),
  async (req, res) => {
    const success = await authenticate(req.body);
    if (success) {
      await req.bruteforceReset?.(); // Clear counter on success
      res.json({ token: "..." });
    } else {
      res.status(401).json({ error: "Invalid credentials" });
    }
  },
);

Response headers set automatically:

  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • X-RateLimit-Reset
  • Retry-After (when blocked)

5. Utilities

import {
  sha256,
  hmacSha256,
  secureRandomHex,
  secureRandomAlphanumeric,
  timingSafeCompare,
  generateId,
  generateRequestFingerprint,
} from "@amine_boutouil/bouncer";

// Request fingerprinting (IP + UA + headers)
const fingerprint = generateRequestFingerprint(req);

// Timing-safe comparison (prevents timing attacks)
const match = timingSafeCompare(inputHash, storedHash);

// Secure random generation
const apiKey = secureRandomHex(32); // 64-char hex string
const code = secureRandomAlphanumeric(16);

6. Error Handling

All Bouncer errors follow a consistent pattern:

import { bouncerErrorHandler } from "@amine_boutouil/bouncer";

// Mount as the LAST middleware
app.use(bouncerErrorHandler);

| Error | Status | Code | | --------------------- | ------ | -------------------------- | | AuthenticationError | 401 | AUTHENTICATION_REQUIRED | | AuthorizationError | 403 | INSUFFICIENT_PERMISSIONS | | RateLimitError | 429 | RATE_LIMIT_EXCEEDED | | OTPError | 400 | INVALID_OTP | | ConfigurationError | 500 | CONFIGURATION_ERROR | | IntegrityError | 500 | INTEGRITY_VIOLATION |

Non-Bouncer errors return a generic 500 response without leaking internal details.


Architecture

src/
├── index.ts                    # Public API barrel export
├── error-handler.ts            # Centralized Express error handler
├── core/
│   ├── config.ts               # Zod-validated configuration schemas
│   ├── errors.ts               # Error class hierarchy
│   └── logger.ts               # Winston structured logger
├── audit/
│   ├── middleware.ts            # createAuditMiddleware()
│   ├── sanitizer.ts            # Recursive sensitive field stripping
│   ├── hash-chain.ts           # SHA256 chain + HMAC
│   ├── integrity.ts            # verifyAuditIntegrity()
│   └── storage/
│       ├── file-adapter.ts     # Append-only file storage with rotation
│       └── memory-adapter.ts   # In-memory adapter (testing)
├── rbac/
│   ├── jwt.ts                  # JWT verification (HS256/RS256)
│   ├── middleware.ts            # authenticate, requireRole, requireGodMode, requirePermission
│   └── hierarchy.ts            # Role hierarchy engine
├── otp/
│   ├── totp.ts                 # TOTP generation & verification
│   ├── qrcode.ts               # QR code generation
│   ├── recovery.ts             # Recovery code generation & verification
│   └── two-factor.ts           # High-level 2FA manager
├── rate-limit/
│   ├── middleware.ts            # Rate limiting middleware
│   └── bruteforce.ts           # Anti-bruteforce guard
├── utils/
│   ├── crypto.ts               # SHA256, HMAC, secure random, timing-safe compare
│   └── fingerprint.ts          # Request fingerprinting
└── types/
    └── index.ts                # All TypeScript type definitions

Security Notes

  • No hardcoded secrets. All secrets must be provided via configuration or environment variables.
  • No insecure defaults. HMAC requires 32+ character secrets. JWT expiration is strictly enforced.
  • Timing-safe comparisons used for all secret/hash comparisons to prevent timing attacks.
  • Sensitive data stripping is recursive and case-insensitive.
  • Recovery codes are hashed (SHA256) before storage — plaintext is shown once.
  • OTP replay protection prevents reuse of the same TOTP token within its time window.
  • Error responses never leak internal details for non-Bouncer errors.
  • Append-only audit logs with hash chain integrity verification and optional HMAC signing.
  • OWASP-aligned security decisions throughout.

Configuration Validation

All module configurations are validated using Zod schemas at initialization time. Invalid configurations throw ConfigurationError immediately rather than failing silently at runtime.


Development

# Install dependencies
npm install

# Type check
npx tsc --noEmit

# Run tests
npm test

# Build
npm run build

# Lint
npm run lint

License

MIT © Amine Boutouil