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

authpool

v3.0.0

Published

Plug-and-play Node.js authentication server with OAuth, JWT, RBAC, CSRF, rate limiting and more.

Readme

AuthPool

A plug-and-play Node.js authentication server. One function call gives you Google OAuth, email/password login, JWT access tokens, rotating refresh tokens, CSRF protection, rate limiting, brute-force lockout, and role-based access control — all production-ready.


Install

npm install authpool

Quickstart

const { startAuthServer } = require("authpool");

startAuthServer({
  mongoURI:      process.env.MONGO_URI,
  jwtSecret:     process.env.JWT_SECRET,
  sessionSecret: process.env.SESSION_SECRET,
});

The server starts at http://localhost:5000. All auth routes are live immediately.


Environment Variables

Create a .env file in your project root. AuthPool loads it automatically — you don't need to call require('dotenv') yourself.

| Variable | Required | Description | |------------------------|----------|--------------------------------------------------| | MONGO_URI | ✅ | MongoDB connection string | | JWT_SECRET | ✅ | JWT signing secret (32+ random characters) | | SESSION_SECRET | ✅ | Session cookie secret (different from JWT) | | GOOGLE_CLIENT_ID | OAuth | Google OAuth client ID | | GOOGLE_CLIENT_SECRET | OAuth | Google OAuth client secret | | GOOGLE_CALLBACK_URL | OAuth | e.g. http://localhost:5000/auth/google/callback| | PORT | — | Listening port (default: 5000) | | CSRF_SECRET | — | Separate CSRF secret (defaults to SESSION_SECRET)| | REDIS_URL | — | Redis connection URL |

Missing required variables cause a clean startup error that lists exactly what's missing.


API Routes

Health

| Method | Path | Description | |--------|-------|-------------------------| | GET | / | Returns { status: "ok" } |

Auth

| Method | Path | Auth required | Description | |--------|-------------------------|---------------|--------------------------------------------------| | GET | /auth/csrf | — | Returns a CSRF token (also sent in response header) | | POST | /auth/register | — | Create account with { email, password, name } | | POST | /auth/login | — | Login with { email, password } | | GET | /auth/google | — | Redirect to Google OAuth consent screen | | GET | /auth/google/callback | — | OAuth callback — handled automatically | | GET | /auth/protected | Bearer JWT | Test route — returns decoded token payload | | GET | /auth/me | Bearer JWT | Returns full user record from MongoDB | | GET | /auth/admin | Bearer JWT + admin role | Admin-only test route | | POST | /auth/refresh | Cookie | Rotate refresh token, get new access token | | GET | /auth/logout | — | Revoke refresh token and clear cookie | | POST | /auth/logout-all | Bearer JWT | Invalidate all tokens across all devices |

Register & Login responses

{ "accessToken": "<jwt>", "roles": ["user"] }

A refreshToken httpOnly cookie is also set automatically.


Full Configuration

const { startAuthServer } = require("authpool");

const { app, server } = await startAuthServer({
  // Required (or set via .env)
  mongoURI:      "mongodb://localhost:27017/myapp",
  jwtSecret:     "super-secret-32-char-minimum",
  sessionSecret: "another-secret-32-char-minimum",

  // Google OAuth (optional)
  googleClientID:     "...",
  googleClientSecret: "...",
  googleCallbackURL:  "http://localhost:5000/auth/google/callback",

  // Server
  port: 5000,

  // CORS
  corsOptions: {
    origin:      "http://localhost:3000",
    methods:     ["GET", "POST"],
    credentials: true,
  },

  // Rate limiting (these are the defaults — only override what you need)
  rateLimit: {
    global:   { windowMs: 15 * 60 * 1000, max: 300 },
    auth:     { windowMs: 60 * 1000,       max: 30  },
    slowdown: { windowMs: 60 * 1000, delayAfter: 3, delayMs: 300 },
  },

  // CSRF (enabled by default)
  csrf: {
    enabled:    true,
    headerName: "x-csrf-token",  // send this header on every mutating request
    cookieName: "authpool.csrf",
    secret:     "optional-separate-csrf-secret",
  },

  // Redis (optional but recommended for production)
  redis: {
    url: "redis://localhost:6379",
    // host: "localhost", port: 6379   ← alternative
    // enabled: false                  ← force in-memory
  },

  // Transform an OAuth profile before the DB upsert (optional)
  transformUser: (profile, provider) => ({
    googleId:   profile.id,
    email:      profile.emails?.[0]?.value,
    name:       profile.displayName,
    profilePic: profile.photos?.[0]?.value,
    roles:      ["user"],
    // add any custom fields here
  }),

  // Add your own routes after AuthPool finishes startup
  onReady: (app, server) => {
    app.get("/api/hello", (req, res) => res.json({ message: "custom route" }));
  },
});

Frontend Usage

1 — Register

const res = await fetch("http://localhost:5000/auth/register", {
  method: "POST",
  headers: { "Content-Type": "application/json", "x-csrf-token": csrfToken },
  credentials: "include",   // sends the cookie
  body: JSON.stringify({ email, password, name }),
});
const { accessToken } = await res.json();

2 — Attach the token to requests

fetch("/api/protected-resource", {
  headers: { Authorization: `Bearer ${accessToken}` },
  credentials: "include",
});

3 — Refresh silently when the token expires

async function refresh() {
  const res = await fetch("http://localhost:5000/auth/refresh", {
    method: "POST",
    credentials: "include",   // the cookie is sent automatically
  });
  const { accessToken } = await res.json();
  return accessToken;
}

4 — CSRF token

Fetch once and reuse. A fresh token is also returned in the x-csrf-token header of every GET response.

const res = await fetch("http://localhost:5000/auth/csrf", { credentials: "include" });
const { csrfToken } = await res.json();
// Include as x-csrf-token header on every POST / PATCH / DELETE

Adding Protected Routes

Use the onReady hook to access the Express app and add routes after startup:

const verifyJWT       = require("authpool/src/middleware/verifyJWT");
const { authorizeRoles } = require("authpool/src/middleware/authorizeRoles");

startAuthServer({
  // ...config...
  onReady: (app) => {
    const JWT_SECRET = process.env.JWT_SECRET;

    // Any authenticated user
    app.get("/api/profile", verifyJWT(JWT_SECRET), (req, res) => {
      res.json({ user: req.user });
    });

    // Admin only
    app.get("/api/admin", verifyJWT(JWT_SECRET), authorizeRoles(["admin"]), (req, res) => {
      res.json({ message: "admin area" });
    });
  },
});

Security Summary

| Feature | Default | |-----------------------|----------------------------------------------| | Password hashing | bcrypt, 12 salt rounds | | Access token expiry | 15 minutes | | Refresh token expiry | 30 days, rotated on every use | | Refresh token storage | SHA-256 hashed in MongoDB, httpOnly cookie | | CSRF protection | Double-submit cookie (csrf-csrf) | | Brute-force lockout | 5 failures → 15-minute IP lockout | | Rate limiting | Global 300/15 min, credential 30/min | | HTTP headers | helmet defaults | | Session storage | MongoDB (connect-mongo), not MemoryStore |


Redis

Redis is optional. Without it everything works using in-process memory (single server only). With it, rate limiters, brute-force counters, and the JWT user cache scale across multiple instances and survive restarts.

# Local Redis via Docker
docker run -d -p 6379:6379 redis:7
REDIS_URL=redis://localhost:6379

Pass redis: { enabled: false } to force in-memory mode even when REDIS_URL is set (useful in tests).


TypeScript

Types are included at authpool/types/index.d.ts. Import AuthPoolOptions for full autocomplete:

import { startAuthServer, AuthPoolOptions } from "authpool";

const options: AuthPoolOptions = { /* ... */ };
await startAuthServer(options);

Requirements

  • Node.js 18+
  • MongoDB 5+ (local or Atlas)
  • Redis (optional, recommended for production)

License

MIT