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

@nandalalshukla/auth-hero

v1.0.2

Published

A complete, secure, production-ready authentication server with email/password, OAuth, MFA (TOTP), session management, and more.

Readme

AuthHero

Drop-in authentication for Express apps. Email/password, OAuth, MFA — all production-ready, fully typed, and secured out of the box.

npm install @nandalalshukla/auth-hero express
import "dotenv/config";
import { createAuthHero } from "@nandalalshukla/auth-hero";

const auth = await createAuthHero();
auth.app.listen(3000);

That's it. You now have register, login, email verification, password reset, OAuth (Google/GitHub/Facebook), MFA (TOTP), session management, and rate limiting — all running on port 3000.


Table of Contents


Prerequisites

  • Node.js >= 18
  • PostgreSQL running locally or remotely
  • Redis running locally or remotely

Installation

npm install @nandalalshukla/auth-hero express

Quick Setup

1. Copy the Prisma schema and run migrations

cp node_modules/@nandalalshukla/auth-hero/prisma/schema.prisma prisma/schema.prisma
npx prisma migrate dev --name init

2. Create your .env file

cp node_modules/@nandalalshukla/auth-hero/.env.example .env

3. Generate secrets

Run this command once for each secret you need to fill in:

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

4. Start your app

import "dotenv/config";
import { createAuthHero } from "@nandalalshukla/auth-hero";

const auth = await createAuthHero();
auth.app.listen(3000, () => console.log("Running on http://localhost:3000"));

5. Verify it works

curl http://localhost:3000/health
# { "status": "ok", "timestamp": "2026-02-27T..." }

Usage

Standalone App

Use the built-in Express app. It comes with Helmet, CORS, cookie parsing, rate limiting, all auth routes, and a global error handler — fully configured.

import "dotenv/config";
import { createAuthHero } from "@nandalalshukla/auth-hero";

const auth = await createAuthHero();

auth.app.listen(3000, () => {
  console.log("AuthHero running on http://localhost:3000");
});

Mount on Your Own Express App

Already have an Express app? Just mount the route modules:

import "dotenv/config";
import express from "express";
import cookieParser from "cookie-parser";
import { createAuthHero } from "@nandalalshukla/auth-hero";

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

const auth = await createAuthHero();

// Mount auth routes
app.use("/auth", auth.routes.auth);
app.use("/auth/oauth", auth.routes.oauth);
app.use("/auth/mfa", auth.routes.mfa);

// Your own routes go here
app.get("/", (_req, res) => res.json({ hello: "world" }));

// Error handler MUST be last
app.use(auth.errorMiddleware);

app.listen(3000);

Protect Your Routes

Use auth.authenticate to guard any route with JWT authentication. The user's ID and session ID are available on req.user.

import { createAuthHero } from "@nandalalshukla/auth-hero";

const auth = await createAuthHero();

// Requires a valid access token in the Authorization header
app.get("/me", auth.authenticate, async (req, res) => {
  const user = await auth.prisma.user.findUnique({
    where: { id: req.user!.userId },
    select: { id: true, email: true, emailVerified: true, mfaEnabled: true },
  });
  res.json(user);
});

// Requires valid access token + verified MFA
app.delete("/account", auth.authenticate, auth.requireMFA, async (req, res) => {
  await auth.prisma.user.delete({ where: { id: req.user!.userId } });
  res.json({ deleted: true });
});

Access the Database

The auth.prisma client gives you full access to the database. Use it for custom queries alongside AuthHero's built-in operations.

// Get all users
const users = await auth.prisma.user.findMany({
  select: { id: true, email: true, createdAt: true },
});

// Get user count
const count = await auth.prisma.user.count();

Graceful Shutdown

Call auth.shutdown() to close database, Redis, and email worker connections cleanly.

const auth = await createAuthHero();
const server = auth.app.listen(3000);

process.on("SIGTERM", async () => {
  server.close();
  await auth.shutdown();
  process.exit(0);
});

API Reference

createAuthHero()

Returns a Promise<AuthHero> with the following properties:

| Property | Type | Description | | ----------------- | --------------------- | -------------------------------------------------- | | app | Express | Full Express app with all routes and middleware | | routes.auth | Router | Auth routes — register, login, verify, reset, etc. | | routes.oauth | Router | OAuth routes — Google, GitHub, Facebook | | routes.mfa | Router | MFA routes — setup, verify, challenge, disable | | authenticate | RequestHandler | JWT middleware — protects routes | | requireMFA | RequestHandler | MFA enforcement middleware | | errorMiddleware | ErrorRequestHandler | Error handler — must be the last middleware | | prisma | PrismaClient | Database client for custom queries | | shutdown() | () => Promise<void> | Close all connections gracefully |

Auth Endpoints

All routes are prefixed with /auth when using the standalone app.

| Method | Path | Auth | Body | Description | | ------ | ------------------ | ---- | ---------------------------------- | --------------------------- | | POST | /register | No | { email, password } | Create account | | POST | /login | No | { email, password } | Login | | POST | /verify-email | No | { token } | Verify email address | | POST | /forgot-password | No | { email } | Send reset email | | POST | /reset-password | No | { token, newPassword } | Reset password | | POST | /change-password | Yes | { currentPassword, newPassword } | Change password | | POST | /refresh-token | No | — | Rotate tokens (uses cookie) | | POST | /logout | Yes | — | Revoke current session | | POST | /logout-all | Yes | — | Revoke all sessions |

Password requirements: min 8 chars, at least one lowercase, one uppercase, one digit, one special character.

OAuth Endpoints

Prefixed with /auth/oauth. Supported providers: google, github, facebook.

| Method | Path | Description | | ------ | --------------------- | ---------------------------------- | | GET | /:provider | Get OAuth authorization URL | | GET | /callback/:provider | OAuth redirect callback (internal) | | POST | /exchange | Exchange one-time code for tokens |

OAuth flow:

  1. Frontend calls GET /auth/oauth/google → gets an authorization URL
  2. Redirect user to that URL → user grants permission
  3. Provider redirects to callback → AuthHero stores tokens in Redis
  4. Frontend receives a one-time code via redirect query param
  5. Frontend calls POST /auth/oauth/exchange with the code → gets access + refresh tokens

MFA Endpoints

Prefixed with /auth/mfa. Uses TOTP (compatible with Google Authenticator, Authy, etc.)

| Method | Path | Auth | Body | Description | | ------ | ------------ | ----- | --------------------- | -------------------------- | | POST | /setup | Yes | — | Get QR code + backup codes | | POST | /verify | Yes | { code } | Verify TOTP and enable MFA | | POST | /challenge | Token | { tempToken, code } | Complete MFA during login | | POST | /disable | Yes | { code } | Disable MFA |

MFA login flow:

  1. POST /auth/login → returns { mfaRequired: true, tempToken: "..." }
  2. POST /auth/mfa/challenge with the temp token + TOTP code → returns access + refresh tokens

Error Codes

All errors follow a consistent format:

{
  "success": false,
  "message": "Human-readable message",
  "errorCode": "MACHINE_READABLE_CODE"
}

| Code | Description | | ---------------------- | ----------------------------------------- | | INVALID_CREDENTIALS | Wrong email or password | | EMAIL_NOT_VERIFIED | Email address not yet verified | | EMAIL_ALREADY_EXISTS | Account with this email already exists | | TOKEN_EXPIRED | JWT or verification token has expired | | TOKEN_INVALID | Token is malformed or tampered with | | TOKEN_ALREADY_USED | One-time token was already consumed | | SESSION_REVOKED | Session was revoked (logged out) | | SESSION_EXPIRED | Session has expired | | RATE_LIMIT_EXCEEDED | Too many requests | | MFA_REQUIRED | MFA verification needed to complete login | | MFA_INVALID_CODE | Wrong TOTP code or backup code | | MFA_NOT_SETUP | MFA is not set up for this account | | VALIDATION_FAILED | Request body failed Zod validation |

You can catch these in your frontend:

if (error.errorCode === "EMAIL_NOT_VERIFIED") {
  showResendVerificationUI();
}

Environment Variables

Create a .env file with the following variables. All JWT/MFA secrets should be random 64-character hex strings.

| Variable | Required | Default | Description | | --------------------------- | -------- | ----------------------- | --------------------------------------------- | | DATABASE_URL | Yes | — | PostgreSQL connection string | | REDIS_HOST | Yes | localhost | Redis host | | REDIS_PORT | Yes | 6379 | Redis port | | PORT | No | 5000 | Server port | | NODE_ENV | No | development | development / production / test | | ACCESS_TOKEN_SECRET | Yes | — | JWT signing key for access tokens | | REFRESH_TOKEN_SECRET | Yes | — | JWT signing key for refresh tokens | | VERIFY_EMAIL_TOKEN_SECRET | Yes | — | JWT key for email verification tokens | | FORGOT_PSWD_TOKEN_SECRET | Yes | — | JWT key for forgot password tokens | | RESET_PSWD_TOKEN_SECRET | Yes | — | JWT key for reset password tokens | | MFA_ENCRYPTION_KEY | Yes | — | 64-char hex string for AES-256-GCM encryption | | MFA_TEMP_TOKEN_SECRET | Yes | — | JWT key for temporary MFA tokens | | EMAIL_HOST | Yes | smtp.gmail.com | SMTP server hostname | | EMAIL_PORT | Yes | 465 | SMTP server port | | EMAIL_USER | Yes | — | SMTP username/email | | EMAIL_PASS | Yes | — | SMTP password or app password | | APP_URL | Yes | http://localhost:5000 | Backend URL (used in OAuth redirects) | | FRONTEND_URL | No | — | Frontend URL (for CORS + redirects) | | GOOGLE_CLIENT_ID | No | — | Google OAuth client ID | | GOOGLE_CLIENT_SECRET | No | — | Google OAuth client secret | | GOOGLE_REDIRECT_URI | No | — | Google OAuth callback URL | | GITHUB_CLIENT_ID | No | — | GitHub OAuth client ID | | GITHUB_CLIENT_SECRET | No | — | GitHub OAuth client secret | | GITHUB_REDIRECT_URI | No | — | GitHub OAuth callback URL | | FACEBOOK_CLIENT_ID | No | — | Facebook OAuth client ID | | FACEBOOK_CLIENT_SECRET | No | — | Facebook OAuth client secret | | FACEBOOK_REDIRECT_URI | No | — | Facebook OAuth callback URL |

Generate a secret:

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

Authentication Flows

Email / Password

POST /auth/register      → Account created, verification email sent
POST /auth/verify-email   → Email verified
POST /auth/login          → Returns accessToken + refreshToken (HTTP-only cookie)
POST /auth/refresh-token  → Rotate tokens (reads cookie)
POST /auth/logout         → Revoke current session

OAuth

GET  /auth/oauth/google        → Authorization URL
  → User redirects to Google   → Grants permission
GET  /auth/oauth/callback/google → One-time code stored in Redis
  → Frontend receives code via redirect
POST /auth/oauth/exchange       → Exchange code → accessToken + refreshToken

MFA (TOTP)

# Setup
POST /auth/mfa/setup    → QR code (base64) + backup codes
  → User scans QR in authenticator app
POST /auth/mfa/verify   → Verify TOTP code → MFA enabled

# Login with MFA
POST /auth/login         → { mfaRequired: true, tempToken: "..." }
POST /auth/mfa/challenge → Verify TOTP → full session tokens

Security

AuthHero is built with security as a first-class concern:

  • Argon2id — Memory-hard password hashing (GPU-resistant)
  • AES-256-GCM — MFA secrets encrypted at rest (never stored as plaintext)
  • SHA-256 hashed refresh tokens — Stored hashed in the database, sent as HTTP-only secure cookies
  • One-time OAuth codes — Tokens never appear in URLs; exchanged via Redis with short TTLs
  • Per-route rate limiting — Prevents brute-force attacks on login, register, password reset
  • Strict CORS — Origin whitelist (no wildcards, no prefix matching)
  • Zod validation — Every request body validated before processing
  • Constant-time comparison — Password verification via Argon2's built-in timing-safe compare
  • Helmet — Security headers (CSP, HSTS, X-Content-Type-Options, etc.)

Boilerplate Mode

Want to fork and customize instead of using it as a package? Clone the repo directly:

git clone https://github.com/nandalalshukla/authhero.git my-app
cd my-app
npm install
cp .env.example .env
# Fill in your .env values
npx prisma migrate dev
npm run dev

This gives you the full source code so you can modify anything — routes, validation, email templates, database schema, etc.


Tech Stack

| Layer | Technology | | ------------- | -------------------------------------------- | | Runtime | Node.js >= 18 | | Framework | Express 5 | | Database | PostgreSQL (Prisma 7 + @prisma/adapter-pg) | | Cache / Queue | Redis (ioredis) + BullMQ | | Auth | JWT (jsonwebtoken) + Argon2 | | MFA | otplib (TOTP) + QRCode | | Validation | Zod 4 | | Email | Nodemailer | | Logging | Pino |


Contributing

See CONTRIBUTING.md for guidelines.

Security Policy

Found a vulnerability? See SECURITY.md for responsible disclosure instructions.

License

MIT