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

api-core-auth

v1.0.4

Published

Secure opaque token authentication for Node.js, Express, JavaScript, and TypeScript APIs with password helpers, middleware, errors, and types.

Readme

api-core-auth

npm version license npm downloads dependencies types node source

Secure opaque-token authentication helpers, password utilities, Express middleware, error classes, and TypeScript types for production Node.js APIs.

api-core-auth is a small authentication toolkit for JavaScript and TypeScript backend projects.

It uses opaque tokens instead of readable token payloads. An opaque token is a secure random string. It does not contain user data, roles, emails, or private information inside the token.

That makes the package simple and safe:

  • Users cannot decode the token and read hidden data.
  • Your server stays in control of token validation.
  • Tokens can be revoked by removing or disabling the stored token hash.
  • Only token hashes need to be stored in your database.
  • The package does not connect to your database or force a user model.

Why Opaque Tokens?

Opaque tokens are a strong default for applications that want users and teams to feel safe.

Use opaque tokens for:

  • Login sessions
  • API authentication
  • Refresh tokens
  • Password reset links
  • Email verification links
  • Invitation links
  • API keys

The token sent to the client is random and unreadable. The server stores a hash of that token and compares it when the token is used again.

Client receives raw token
Server stores token hash
Client sends raw token in Authorization header
Server hashes and compares it with stored hash

What This Package Does

api-core-auth provides reusable authentication helpers:

  • Generate secure opaque tokens.
  • Hash opaque tokens before storage.
  • Compare opaque tokens with stored hashes.
  • Extract Bearer tokens from request headers.
  • Hash passwords with bcrypt.
  • Compare passwords with bcrypt.
  • Validate password strength.
  • Protect Express routes.
  • Support optional authentication.
  • Restrict routes by role.
  • Sanitize user objects.
  • Create consistent auth responses.
  • Provide safe authentication error classes.
  • Provide TypeScript types.

What This Package Does Not Do

This package intentionally does not own your application logic.

It does not:

  • Connect to a database.
  • Register users.
  • Find users by email.
  • Validate login credentials from a database.
  • Store tokens for you.
  • Revoke tokens for you.
  • Decide your user schema.
  • Manage sessions for you.

Your application is responsible for:

  • Creating the first admin user.
  • User registration.
  • User lookup.
  • Database queries.
  • Storing token hashes.
  • Token expiration policy.
  • Token revocation.
  • Application-specific authorization rules.

This keeps the package secure, flexible, and easy to use in different backend projects.

Recommended Application Flow

api-core-auth should be used inside your application auth flow. It should not run by itself, create users, or create a default admin account.

The recommended flow is:

1. Create or seed a user in your application
2. Hash the user's password with hashPassword()
3. Store the user and password hash in your database
4. On login, find the user from your database
5. Compare the submitted password with comparePassword()
6. Create an opaque token with createOpaqueToken()
7. Store only tokenHash in your database
8. Send token to the client
9. Protect routes with authMiddleware()
10. Revoke access by deleting or disabling the stored token hash

Admin User Flow

For production apps, create the first admin user in your own application.

Good options:

  • Database seed script
  • Secure setup command
  • Private admin invite flow
  • Manual admin creation from an internal tool

Avoid auto-creating an admin user inside an npm package. A reusable package should not secretly create users, choose default credentials, or control your database.

Example admin setup flow:

First app setup
→ your app creates [email protected]
→ your app stores the hashed password
→ admin logs in
→ api-core-auth creates an opaque token
→ your app stores the token hash
→ admin uses the raw token to access protected routes

Register Flow

import { hashPassword, sanitizeUser } from "api-core-auth";

const passwordHash = await hashPassword(req.body.password, 12);

const user = await db.users.create({
  data: {
    email: req.body.email,
    password: passwordHash,
    role: "USER"
  }
});

return res.status(201).json({
  success: true,
  message: "User registered successfully",
  data: {
    user: sanitizeUser(user)
  }
});

Login Flow

import { comparePassword, createOpaqueToken, sanitizeUser } from "api-core-auth";

const user = await db.users.findUnique({
  where: {
    email: req.body.email
  }
});

if (!user) {
  return res.status(401).json({
    success: false,
    message: "Invalid email or password"
  });
}

const isValidPassword = await comparePassword(req.body.password, user.password);

if (!isValidPassword) {
  return res.status(401).json({
    success: false,
    message: "Invalid email or password"
  });
}

const authToken = createOpaqueToken({
  pepper: process.env.OPAQUE_TOKEN
});

await db.sessions.create({
  data: {
    userId: user.id,
    tokenHash: authToken.tokenHash,
    expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7)
  }
});

return res.json({
  success: true,
  message: "Login successful",
  data: {
    user: sanitizeUser(user),
    token: authToken.token
  }
});

Protected Route Flow

import { authMiddleware, hashOpaqueToken } from "api-core-auth";

app.get(
  "/profile",
  authMiddleware({
    verifyToken: async (token) => {
      const tokenHash = hashOpaqueToken(token, process.env.OPAQUE_TOKEN);

      const session = await db.sessions.findUnique({
        where: {
          tokenHash
        },
        include: {
          user: true
        }
      });

      if (!session || session.expiresAt < new Date()) {
        return null;
      }

      return session.user;
    }
  }),
  (req, res) => {
    res.json({
      success: true,
      data: req.user
    });
  }
);

Logout Flow

import { hashOpaqueToken } from "api-core-auth";

const tokenHash = hashOpaqueToken(tokenFromRequest, process.env.OPAQUE_TOKEN);

await db.sessions.delete({
  where: {
    tokenHash
  }
});

return res.json({
  success: true,
  message: "Logged out successfully"
});

Installation

npm install api-core-auth

If you want to use the Express middleware, install Express in your application:

npm install express

Environment Variables

Use a server-side pepper when hashing opaque tokens. Keep it secret.

OPAQUE_TOKEN=replace-with-a-long-random-server-side-secret

The pepper is optional, but recommended. It adds an extra server-side secret to token hashing.

Quick Start

This example uses an in-memory Map to keep the example simple. In a real application, store token hashes in your database.

const express = require("express");
const {
  authMiddleware,
  compareOpaqueToken,
  comparePassword,
  createOpaqueToken,
  roleMiddleware,
  sanitizeUser
} = require("api-core-auth");

const app = express();
const opaqueTokenSecret = process.env.OPAQUE_TOKEN;
const tokenStore = new Map();

app.use(express.json());

app.post("/login", async (req, res) => {
  const user = {
    id: "1",
    email: "[email protected]",
    password: "$2b$10$hashedPassword",
    role: "ADMIN"
  };

  const isValidPassword = await comparePassword(req.body.password, user.password);

  if (!isValidPassword) {
    return res.status(401).json({
      success: false,
      message: "Invalid email or password"
    });
  }

  const safeUser = sanitizeUser(user);
  const authToken = createOpaqueToken({
    pepper: opaqueTokenSecret
  });

  tokenStore.set(user.id, {
    tokenHash: authToken.tokenHash,
    user: safeUser
  });

  return res.json({
    success: true,
    message: "Login successful",
    data: {
      user: safeUser,
      token: authToken.token
    }
  });
});

const verifyStoredToken = (token) => {
  for (const session of tokenStore.values()) {
    if (compareOpaqueToken(token, session.tokenHash, opaqueTokenSecret)) {
      return session.user;
    }
  }

  return null;
};

app.get(
  "/profile",
  authMiddleware({
    verifyToken: verifyStoredToken
  }),
  (req, res) => {
    res.json({
      success: true,
      data: req.user
    });
  }
);

app.get(
  "/admin",
  authMiddleware({
    verifyToken: verifyStoredToken
  }),
  roleMiddleware(["ADMIN"]),
  (_req, res) => {
    res.json({
      success: true,
      message: "Admin route accessed successfully"
    });
  }
);

Example Login Response

{
  "success": true,
  "message": "Login successful",
  "data": {
    "user": {
      "id": "1",
      "email": "[email protected]",
      "role": "ADMIN"
    },
    "token": "N03tY5gO9B4oPz0V6xJhO3dYb-4QmYQ9u0zV2S3xZ3M"
  }
}

The response sends the raw token once. Store only the token hash on the server.

JavaScript Usage

const {
  createOpaqueToken,
  compareOpaqueToken,
  hashPassword,
  comparePassword,
  authMiddleware,
  roleMiddleware
} = require("api-core-auth");

TypeScript Usage

import {
  createOpaqueToken,
  compareOpaqueToken,
  hashPassword,
  comparePassword,
  authMiddleware,
  roleMiddleware,
  type AuthUser
} from "api-core-auth";

Framework Compatibility

api-core-auth works with any Node.js backend through its framework-independent helpers.

Ready-to-use middleware is included for:

  • Express.js
  • Express-compatible frameworks
  • NestJS projects using the Express adapter

The core helpers can be used with:

  • Fastify
  • Koa
  • Hapi
  • Next.js API routes
  • Nuxt/Nitro server routes
  • AdonisJS
  • Native Node.js HTTP servers
  • Custom Node.js backends

For non-Express frameworks, use extractBearerToken(), compareOpaqueToken(), hashPassword(), and comparePassword() inside your own middleware, hook, guard, or request handler.

Features

Opaque Token Helpers

| Function | Purpose | | --- | --- | | generateOpaqueToken() | Create a secure random token with no readable payload. | | hashOpaqueToken() | Hash an opaque token before storing it. | | compareOpaqueToken() | Compare a raw opaque token with a stored hash. | | createOpaqueToken() | Create a raw opaque token and storage-safe hash together. | | extractBearerToken() | Extract a token from a Bearer authorization header. |

Password Helpers

| Function | Purpose | | --- | --- | | hashPassword() | Hash a password with bcryptjs. | | comparePassword() | Compare a plain password with a hash. | | validatePasswordStrength() | Validate password strength rules. |

Express Middleware

| Function | Purpose | | --- | --- | | authMiddleware() | Require a valid opaque token and attach req.user. | | optionalAuthMiddleware() | Attach req.user when a valid token exists, but allow guests. | | roleMiddleware() | Require one or more allowed roles. |

Utilities

| Function | Purpose | | --- | --- | | sanitizeUser() | Remove sensitive fields from a user object. | | createAuthResponse() | Create a consistent auth success response. | | getAuthUser() | Read req.user or throw UnauthorizedError. |

Error Classes

  • AuthError
  • UnauthorizedError
  • ForbiddenError
  • InvalidTokenError
  • TokenExpiredAuthError

TypeScript Types

  • AuthUser
  • TokenPair
  • AuthMiddlewareOptions
  • Role

Opaque Token Examples

Create a Token

import { createOpaqueToken } from "api-core-auth";

const authToken = createOpaqueToken({
  byteLength: 32,
  pepper: process.env.OPAQUE_TOKEN
});

// Send authToken.token to the client.
// Store authToken.tokenHash in your database.

Verify a Token

import { compareOpaqueToken } from "api-core-auth";

const isValidToken = compareOpaqueToken(
  tokenFromRequest,
  storedTokenHash,
  process.env.OPAQUE_TOKEN
);

Extract a Bearer Token

import { extractBearerToken } from "api-core-auth";

const token = extractBearerToken(req.headers.authorization);

Password Reset Token

import { createOpaqueToken } from "api-core-auth";

const resetToken = createOpaqueToken({
  byteLength: 32,
  pepper: process.env.OPAQUE_TOKEN
});

// Email resetToken.token to the user.
// Store resetToken.tokenHash with an expiration date in your database.

Password Examples

Hash a Password

import { hashPassword } from "api-core-auth";

const passwordHash = await hashPassword("Password123", 12);

Compare a Password

import { comparePassword } from "api-core-auth";

const isValidPassword = await comparePassword("Password123", passwordHash);

Validate Password Strength

import { validatePasswordStrength } from "api-core-auth";

const result = validatePasswordStrength("Password123", {
  minLength: 8,
  requireUppercase: true,
  requireLowercase: true,
  requireNumber: true,
  requireSymbol: false
});

if (!result.valid) {
  console.log(result.errors);
}

Express Middleware

Required Auth

authMiddleware() requires a valid token. Your application provides verifyToken, which checks the raw token against your stored token hash and returns the authenticated user.

import { authMiddleware, hashOpaqueToken } from "api-core-auth";

app.get(
  "/profile",
  authMiddleware({
    verifyToken: async (token) => {
      const tokenHash = hashOpaqueToken(token, process.env.OPAQUE_TOKEN);
      const session = await db.sessions.findUnique({
        where: {
          tokenHash
        },
        include: {
          user: true
        }
      });

      if (!session) {
        return null;
      }

      return session.user;
    }
  }),
  (req, res) => {
    res.json({
      success: true,
      data: req.user
    });
  }
);

Optional Auth

Use optional authentication for routes that support both guests and signed-in users.

import { optionalAuthMiddleware } from "api-core-auth";

app.get(
  "/feed",
  optionalAuthMiddleware({
    verifyToken: verifyStoredToken
  }),
  (req, res) => {
    res.json({
      success: true,
      data: {
        authenticated: Boolean(req.user),
        user: req.user || null
      }
    });
  }
);

Role-Protected Route

import { authMiddleware, roleMiddleware } from "api-core-auth";

app.get(
  "/admin",
  authMiddleware({
    verifyToken: verifyStoredToken
  }),
  roleMiddleware(["ADMIN"]),
  (_req, res) => {
    res.json({
      success: true,
      message: "Admin route accessed successfully"
    });
  }
);

roleMiddleware() checks both req.user.role and req.user.roles.

Utility Examples

Sanitize a User

import { sanitizeUser } from "api-core-auth";

const safeUser = sanitizeUser({
  id: "1",
  email: "[email protected]",
  password: "secret",
  role: "ADMIN"
});

Create an Auth Response

import { createAuthResponse } from "api-core-auth";

const response = createAuthResponse({
  user: safeUser,
  token: authToken.token,
  message: "Login successful"
});

Get the Authenticated User

import { getAuthUser } from "api-core-auth";

const currentUser = getAuthUser(req);

Error Handling

import { AuthError } from "api-core-auth";

app.use((err, _req, res, _next) => {
  if (err instanceof AuthError) {
    return res.status(err.statusCode).json({
      success: false,
      message: err.message,
      code: err.code
    });
  }

  return res.status(500).json({
    success: false,
    message: "Internal server error"
  });
});

API Reference

Opaque Tokens

| API | Description | | --- | --- | | generateOpaqueToken(options?) | Creates a secure random token with no readable payload. | | hashOpaqueToken(token, pepper?) | Creates a SHA-256 hash for database storage. | | compareOpaqueToken(token, tokenHash, pepper?) | Compares a raw token with a stored hash using constant-time comparison. | | createOpaqueToken(options?) | Returns { token, tokenHash } for send-and-store flows. | | extractBearerToken(header) | Returns a token from a valid Bearer header or null. |

Password

| API | Description | | --- | --- | | hashPassword(password, saltRounds?) | Hashes a password. Default salt rounds: 10. | | comparePassword(password, passwordHash) | Compares a password with a bcrypt hash. | | validatePasswordStrength(password, options?) | Returns password strength status, score, and errors. |

Middleware

| API | Description | | --- | --- | | authMiddleware(options) | Requires a valid token and attaches req.user. | | optionalAuthMiddleware(options) | Allows guests and attaches req.user when a valid token exists. | | roleMiddleware(roles) | Requires the authenticated user to have an allowed role. |

Utilities

| API | Description | | --- | --- | | sanitizeUser(user, sensitiveKeys?) | Removes sensitive fields from user objects. | | createAuthResponse(options) | Creates a consistent auth success response. | | getAuthUser(req) | Returns req.user or throws UnauthorizedError. |

TypeScript Support

The package includes TypeScript types for safer application code.

import type { AuthMiddlewareOptions, AuthUser, Role, TokenPair } from "api-core-auth";

Express request augmentation is included, so req.user is available after using the middleware.

Security Notes

  • Opaque tokens do not contain readable user data.
  • Store only token hashes in your database.
  • Send the raw token to the client only once.
  • Use a server-side pepper when possible.
  • Add expiration dates to stored tokens.
  • Revoke tokens by deleting or disabling their stored hash.
  • Never return password fields in API responses.
  • Return generic login errors such as Invalid email or password.
  • Use HTTPS in production.
  • Avoid returning raw internal errors to API users.
  • Keep database lookup and token storage inside your application.

Contributing

Contributions are welcome. Please keep this package focused on reusable authentication helpers, middleware, errors, utilities, and types.

See CONTRIBUTING.md for contribution details.

License

MIT License.

Copyright (c) 2026 Choch Kimhour.