Catalog npm
PACKAGE EXPLORERView in registry

@0-auth/zero-auth

npm

Minimal JWT authentication for Node.js and Express APIs with refresh-token rotation, HTTP-only cookies, and RBAC

access-tokenapiauthenticationauthorizationcookieexpressexpress-authenticationhttp-only-cookiejwtjwt-authjwt-authenticationmiddlewarenodejsrbacrefresh-tokenrefresh-token-rotation
All keywords (18)
token-revocationtypescript
Versions collected11
Direct dependencies15From release 1.6.0
Last observed20 Oct, 21:53 UTC

README

Current package documentation

@0-auth/zero-auth

Minimal JWT authentication for Node.js and Express APIs.

No database. No hosted auth service. Just access tokens, refresh-token rotation, HTTP-only cookies, and RBAC with middleware your team can understand.

⚠️ Attention — Breaking Changes & Upgrade Notes

[!NOTE] Any release with breaking changes will list them in this section before the rest of the README. Legacy isRevoked + revokeRefreshToken hooks remain supported for compatibility, but emit a warning and are not concurrency-safe. Use the atomic consumeRefreshToken hook for rotated refresh tokens, especially in multi-instance deployments. The new RefreshTokenStore API is the recommended integration for shared rotation state.

Start here: 5-minute quick start · runnable examples · API reference

Documentation: zero-auth.netlify.app · guides · API reference

npm version npm downloads CI License: MIT TypeScript Node.js

JWT auth with refresh-token rotation and reuse detection for teams that want to keep identity and storage in their own application.


Table of Contents


Features

  • Asymmetric & Symmetric Crypto: Sign and verify with modern asymmetric keys (ES256, RS256, EdDSA, PS256) or standard HMAC (HS256, HS384, HS512).
  • Remote JWKS & Dynamic Verification: Verify tokens against remote JWKS endpoints (/.well-known/jwks.json, Clerk, Auth0, Firebase, AWS Cognito) with automated caching, cooldown, and key rotation.
  • JWKS Publisher: Serve your public keys dynamically with auth.jwksHandler() or export JWKS with auth.getJwks().
  • Verifier-Only Mode: Zero-signing verification mode for downstream microservices and API gateways.
  • JWT Validation Policy: Optionally enforce token issuer, audience, clock tolerance, and nbf validation.
  • Pluggable Refresh Stores: Connect atomic refresh-token consumption and family revocation to Redis or another store.
  • ⚡ Zero-Boilerplate Setup: Initialize with createAuth() and start securing routes immediately.
  • 🔒 Secure by Default: Cryptographically signed tokens via jose, strict algorithm restriction, and enforced secret length.
  • 🍪 Built-in Cookie Support: Seamless HTTP-only cookie handling without extra cookie middleware dependencies.
  • 🛡️ Cookie CSRF Protection: Signed double-submit middleware for state-changing cookie requests.
  • 🔄 Automatic Token Refresh & Rotation: Built-in refresh route handler with optional token family reuse detection.
  • 🛡️ Role-Based Access Control (RBAC): Flexible role-checking middleware (auth.authorize(['admin', 'editor'])).
  • 🔑 Permission-Based Access Control: Require fine-grained permissions with auth.authorizePermissions(['users:read']).
  • 🧩 First-Class TypeScript: Automatic req.user typing via declaration merging with support for custom claims.
  • 🛑 Structured Error Handling: Standardized AuthError class with typed error codes (AUTH_TOKEN_EXPIRED, AUTH_FORBIDDEN, etc.).
  • 🌐 Modern & Edge-Ready: Dual ESM & CommonJS builds, Node 18+, Bun, serverless, and edge runtime compatible.

Scope

zero-auth is an authentication layer, not a complete identity platform. It does not provide a user database, password hashing, OAuth providers, email verification, password reset, MFA, or user-management UI. Your application owns those decisions and supplies the user identity used in token claims.


Security model

ConcernDefault approach
Access tokensShort-lived JWTs, typically 15m
Refresh tokensLonger-lived tokens, typically 7d
RotationEnable refreshOptions.rotate when replay detection matters
RevocationYour Redis, database, or other application store
Browser storageHTTP-only cookies when a cookie flow is appropriate

zero-auth does not choose or manage your user database. Read the full security checklist before deploying to production.


Installation

npm install @0-auth/zero-auth
# or
yarn add @0-auth/zero-auth
# or
pnpm add @0-auth/zero-auth

Note: If using Express middleware, install express (peer dependency):

npm install express

Quick Start (5 Minutes)

Here is a complete, copy-pasteable Express application using zero-auth:

Set JWT_ACCESS_SECRET and JWT_REFRESH_SECRET to different random values of at least 32 characters before starting the app.

import express from "express";
import { createAuth } from "@0-auth/zero-auth";

// 1. Initialize Auth instance
const auth = createAuth({
  accessSecret: process.env.JWT_ACCESS_SECRET!,
  refreshSecret: process.env.JWT_REFRESH_SECRET!,
  accessExpiresIn: "15m",
  refreshExpiresIn: "7d",
});

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

// 2. Login Route (generates and sends token pair or cookies)
app.post("/auth/login", async (req, res, next) => {
  try {
    const { email, password } = req.body;
    // ... validate credentials against your database ...
    const userPayload = { id: "user-123", email, role: "admin" };

    // Option A: Set HTTP-Only cookies + return tokens
    const tokens = await auth.sendAuthTokens(res, userPayload);
    res.json({ message: "Logged in successfully", tokens });

    // Option B (Bearer only): const tokens = await auth.generateTokenPair(userPayload); res.json(tokens);
  } catch (err) {
    next(err);
  }
});

// 3. Protected Route (requires valid access token)
app.get("/api/profile", auth.protect(), (req, res) => {
  // req.user is automatically typed as AuthUser!
  res.json({ user: req.user });
});

// 4. Role-Restricted Route (Admin only)
app.get("/api/admin/dashboard", auth.protect(), auth.authorize(["admin"]), (req, res) => {
  res.json({ message: `Welcome Admin ${req.user?.id}` });
});

// 5. Refresh Token Route
app.post("/auth/refresh", auth.refreshHandler());

// 6. Logout Route (clears HTTP-Only cookies)
app.post("/auth/logout", (req, res) => {
  auth.clearAuth(res);
  res.json({ message: "Logged out successfully" });
});

// 7. Error handling middleware (must be registered after routes)
app.use(auth.errorHandler);

app.listen(3000, () => console.log("Server running on http://localhost:3000"));

Examples

Three complete, runnable example projects are included in the examples/ directory:

ExampleDescriptionFeatures
express-rest-apiStateless Bearer token API for mobile / CLI / SPA clientsgenerateTokenPair, protect, authorize, optional, refreshHandler, decodeToken
express-cookies-redisCookie-based auth with Redis-backed refresh token rotationsendAuthTokens, clearAuth, CSRF, rotation hooks, family revocation, onRefreshReuse
express-jwks-microservicesCentral Auth (RS256) + Downstream Microservices via Remote JWKSjwksUri, jwksHandler, getJwks, Verifier-Only mode, dynamic key rotation

Each example includes a README with setup instructions and cURL commands for every endpoint.

For the complete documentation map, read the documentation site. It covers bearer and cookie clients, CSRF, roles and permissions, refresh rotation, errors, deployment, testing, versioning, and releases.

# Quick start (REST API example)
cd examples/express-rest-api
npm ci
npm start

Configuration Options

Pass your settings to createAuth(config):

const auth = createAuth({
  // Option A: Symmetric HMAC (default)
  accessSecret: process.env.JWT_ACCESS_SECRET!,   // String (min 32 chars)
  refreshSecret: process.env.JWT_REFRESH_SECRET!, // String (min 32 chars, distinct from accessSecret)

  // Option B: Asymmetric (RS256, ES256, EdDSA, PS256)
  // algorithm: "ES256",
  // privateKey: process.env.JWT_PRIVATE_KEY, // PKCS#8 PEM string or CryptoKey
  // publicKey: process.env.JWT_PUBLIC_KEY,   // SPKI PEM string or CryptoKey (required)
  // keyId: "auth-key-v1",                    // Optional 'kid' header in JWTs
  // csrfSecret: process.env.CSRF_SECRET,     // Required if using cookie CSRF with asymmetric keys
  //
  // Or Remote JWKS Verification (Verifier-Only Mode):
  // jwksUri: "https://auth.example.com/.well-known/jwks.json",
  // jwks: {
  //   cacheMaxAge: 600_000,       // 10 minutes cache
  //   cooldownDuration: 30_000,   // 30 seconds cooldown between rate-limited refetches
  //   timeoutDuration: 5_000,     // 5 seconds fetch timeout
  // },

  // Optional JWT validation policy
  jwt: {
    issuer: "https://api.example.com",
    audience: "web-app",
    clockTolerance: 5, // Allowed clock skew in seconds
  },

  // Optional Token Expirations (defaults shown)
  accessExpiresIn: "15m",   // Formats: "15m", "1h", "7d", "30d", or milliseconds
  refreshExpiresIn: "7d",

  // Optional Cookie Configuration
  cookies: {
    accessTokenName: "access_token",   // Cookie name for access token
    refreshTokenName: "refresh_token", // Cookie name for refresh token
    options: {
      httpOnly: true,        // Always enforced as true for security
      secure: true,          // Defaults to true when NODE_ENV === 'production'
      sameSite: "lax",       // 'lax' | 'strict' | 'none'
      path: "/",             // Cookie path
      domain: undefined,     // Optional cookie domain
    },
  },

  // Optional CSRF protection for cookie-authenticated requests
  csrf: {
    cookieName: "csrf_token",       // Client-readable token cookie
    headerName: "x-csrf-token",     // Header copied from that cookie
    methods: ["POST", "PUT", "PATCH", "DELETE"],
  },

  // Optional Refresh Rotation & Revocation
  refreshOptions: {
    rotate: false,           // Set to true to enable Refresh Token Rotation
    // Required when rotate: true: implement an atomic single-use check.
    // consumeRefreshToken: (oldJti, ctx) => redis.set(..., { NX: true }).then(result => result === "OK"),
    registerRefreshToken: async (newJti, ctx) => {},
    onRefreshReuse: async (ctx) => {},
  },
});

When configured, jwt.issuer and jwt.audience are included in new tokens and must match during verification. clockTolerance allows limited clock skew in seconds. JWT nbf claims are also validated automatically.


Usage Guides

1. Header / Bearer Token Auth

For mobile apps, CLI tools, or traditional REST APIs sending tokens via headers:

// Login: Return raw tokens in JSON response
app.post("/auth/login", async (req, res) => {
  const tokens = await auth.generateTokenPair({
    id: "user_123",
    email: "user@example.com",
    role: "user",
  });

  res.json(tokens);
  // { accessToken: "eyJ...", refreshToken: "eyJ..." }
});

// Client sends: Authorization: Bearer <accessToken>
app.get("/api/data", auth.protect(), (req, res) => {
  res.json({ data: "Protected content", userId: req.user.id });
});

For web applications (SPAs, React, Next.js, Vue) to protect against XSS token theft:

// Login: Sets secure HTTP-Only cookies on the response automatically
app.post("/auth/login", async (req, res) => {
  const user = { id: "user_123", role: "member" };
  const tokens = await auth.sendAuthTokens(res, user);
  res.json({ user, tokens });
});

// Protected routes automatically read tokens from either Cookie or Authorization header
app.get("/api/data", auth.protect(), (req, res) => {
  res.json({ user: req.user });
});

// Logout: Clears both cookies using the exact same path and options
app.post("/auth/logout", (req, res) => {
  auth.clearAuth(res);
  res.json({ message: "Logged out" });
});

Cookie-authenticated state-changing requests should include a CSRF token. Add the middleware before your routes and expose a small same-origin endpoint that sets and returns the client-readable token:

app.use(auth.csrf());

app.get("/auth/csrf-token", (req, res) => {
  res.json({ csrfToken: auth.csrfToken(res) });
});

app.post("/api/profile", auth.protect(), (req, res) => {
  res.json({ updated: true, user: req.user });
});

The browser sends the token in the configured header on POST, PUT, PATCH, and DELETE requests:

const { csrfToken } = await fetch("/auth/csrf-token", {
  credentials: "include",
}).then((response) => response.json());

await fetch("/api/profile", {
  method: "POST",
  credentials: "include",
  headers: { "x-csrf-token": csrfToken },
});

Requests without auth cookies and safe methods such as GET pass through. The middleware does not protect bearer-token requests because browsers do not send their Authorization header automatically. Mount app.use(auth.csrf()) before state-changing routes, and keep app.use(auth.errorHandler) last.


3. Route Protection & RBAC

Protect routes and limit access by user roles:

// Single role requirement
app.get("/admin", auth.protect(), auth.authorize(["admin"]), adminHandler);

// Multiple allowed roles (user must have at least one)
app.get(
  "/reports",
  auth.protect(),
  auth.authorize(["admin", "manager", "auditor"]),
  reportsHandler
);

Note: auth.authorize() checks req.user.role. Always place auth.protect() before auth.authorize() in the middleware chain.

For fine-grained access checks, require every permission listed in the user's permissions claim:

app.get(
  "/users",
  auth.protect(),
  auth.authorizePermissions(["users:read"]),
  listUsersHandler
);

auth.authorizePermissions() also requires auth.protect() first and returns AUTH_FORBIDDEN when any required permission is missing.


4. Optional Authentication

Use auth.optional() for endpoints that serve both guests and authenticated users (e.g. public articles with personalized like buttons):

app.get("/articles/:slug", auth.optional(), (req, res) => {
  if (req.user) {
    // Authenticated user
    res.json({ article: getArticle(), bookmarked: true });
  } else {
    // Guest visitor (req.user is undefined)
    res.json({ article: getArticle(), bookmarked: false });
  }
});

5. Token Refresh & Rotation

zero-auth includes a ready-to-use Express handler for refreshing tokens:

app.post("/auth/refresh", auth.refreshHandler());

Use refreshOptions.resolveUser when refreshes must reflect current account state instead of claims stored in an older token. Return null to reject a deleted, disabled, or unverified user:

const auth = createAuth({
  accessSecret: process.env.JWT_ACCESS_SECRET!,
  refreshSecret: process.env.JWT_REFRESH_SECRET!,
  refreshOptions: {
    resolveUser: async ({ id }) => {
      const user = await users.findById(id);
      if (!user?.active || !user.emailVerified) return null;
      return { id: user.id, email: user.email, role: user.role };
    },
  },
});

The returned claims replace the old application claims. The user id and refresh token family remain bound to the verified refresh token.

How the refresh token is extracted:

  1. Refresh token cookie (cookies.refreshTokenName)
  2. JSON body field (refreshToken or refresh_token)
  3. Authorization: Bearer <refreshToken> header

Advanced: Refresh Token Rotation (with Redis)

When refreshOptions.rotate: true is enabled, a new refresh token is issued on every refresh request, and old tokens are invalidated. If an old token is reused (indicating a stolen token), onRefreshReuse is triggered to invalidate the entire token family.

consumeRefreshToken should atomically mark the incoming jti as consumed and return false when it was already consumed. Use a Redis SET NX or equivalent database conditional write when the application runs on multiple instances. You can pass a public refreshStore instead; its atomic consume() method and optional register() / revokeFamily() methods are wired into rotation automatically. Explicit refresh hooks still take precedence. The legacy isRevoked + revokeRefreshToken pair remains supported for compatibility, but logs a warning and is not concurrency-safe.

Install ioredis in your application, then use the built-in adapter. The package does not add a Redis client dependency to your application:

npm install @0-auth/zero-auth ioredis
import Redis from "ioredis";
import {
  createAuth,
  createRedisRevocationStore,
} from "@0-auth/zero-auth";

const redis = new Redis(process.env.REDIS_URL);
const refreshStore = createRedisRevocationStore(redis);

const auth = createAuth({
  accessSecret: process.env.JWT_ACCESS_SECRET!,
  refreshSecret: process.env.JWT_REFRESH_SECRET!,
  refreshStore,
  refreshOptions: {
    rotate: true,
    onRefreshReuse: async ({ familyId }) => {
      if (familyId) await refreshStore.revokeFamily(familyId);
    },
  },
});

The adapter uses Redis SET NX for atomic single-use consumption and tracks refresh-token families with TTLs. It accepts the ioredis-compatible client surface without importing or bundling a Redis client. When a family is compromised, it writes a marker before scanning known members and atomically rejects late replacement registrations, so a concurrent replay cannot leave a new usable refresh token behind.


6. Asymmetric Keys & Microservices (RS256 / ES256 / EdDSA)

In distributed architectures, sharing symmetric secrets across multiple services is a security risk. With asymmetric cryptography:

  • The Auth Service holds the private key to sign tokens.
  • Downstream Microservices & Gateways hold only the public key to verify incoming tokens without needing the private signing key.

Supported Asymmetric Algorithms:

  • ECDSA: ES256 (recommended for modern web APIs), ES384, ES512
  • RSA: RS256, RS384, RS512, PS256, PS384, PS512
  • Edwards Curve: EdDSA (Ed25519)

Central Auth Service (Issuing & Verifying)

const auth = createAuth({
  algorithm: "ES256",
  privateKey: process.env.JWT_PRIVATE_KEY!, // PKCS#8 PEM string or CryptoKey
  publicKey: process.env.JWT_PUBLIC_KEY!,   // SPKI PEM string or CryptoKey
  keyId: "auth-key-2026-v1",                // Sets 'kid' in protected header
});

const tokens = await auth.generateTokenPair({ id: "user_123", role: "admin" });

Downstream Service (Verifier-Only Mode)

const auth = createAuth({
  algorithm: "ES256",
  publicKey: process.env.JWT_PUBLIC_KEY!, // Only public key needed!
  jwt: {
    issuer: "https://auth.example.com",
    audience: "api.example.com",
  },
});

// Protect routes without the ability to forge or sign tokens:
app.get("/api/reports", auth.protect(), auth.authorize(["admin"]), (req, res) => {
  res.json({ message: "Secure data", user: req.user });
});

7. Remote JWKS Verification & JWKS Publisher

Downstream Consumer: Dynamic Remote JWKS Verification

Downstream APIs and microservices can dynamically verify tokens issued by central auth servers or third-party Identity Providers (Clerk, Auth0, Firebase, Google, AWS Cognito) using their public /.well-known/jwks.json endpoint.

Keys are cached in-memory and refetched automatically when a new key ID (kid) is encountered, ensuring zero downtime during key rotation.

import { createAuth } from "@0-auth/zero-auth";

const auth = createAuth({
  jwksUri: "https://auth.example.com/.well-known/jwks.json",
  jwt: {
    issuer: "https://auth.example.com",
    audience: "api.example.com",
  },
  jwks: {
    cacheMaxAge: 600_000,     // 10 min cache
    cooldownDuration: 30_000, // 30 sec cooldown between fetches
    timeoutDuration: 5_000,   // 5 sec network timeout
  },
});

// Use protect() middleware as usual:
app.get("/api/orders", auth.protect(), (req, res) => {
  res.json({ orders: [], userId: req.user.id });
});

Central Auth Service: Serving a JWKS Endpoint (Publisher)

If your central service signs tokens with asymmetric keys (RS256, ES256, EdDSA), you can publish the public keys via a standard JWKS route using auth.jwksHandler():

import express from "express";
import { createAuth } from "@0-auth/zero-auth";

const auth = createAuth({
  algorithm: "RS256",
  keyId: "auth-key-2026-v1",
  privateKey: process.env.JWT_PRIVATE_KEY!,
  publicKey: process.env.JWT_PUBLIC_KEY!,
});

const app = express();

// Expose public JWKS endpoint for downstream microservices:
app.get("/.well-known/jwks.json", auth.jwksHandler());

// Or export JWKS programmatically:
const jwks = await auth.getJwks();
// { keys: [ { kty: "RSA", kid: "auth-key-2026-v1", alg: "RS256", use: "sig", n: "...", e: "AQAB" } ] }

Error Handling & Error Codes

All errors thrown by zero-auth are instances of AuthError.

Built-in Error Handler Middleware

// Mount after all routes
app.use(auth.errorHandler);

Default JSON error response format:

{
  "error": {
    "code": "AUTH_TOKEN_EXPIRED",
    "message": "Token has expired",
    "statusCode": 401
  }
}

Manual Error Handling

import { isAuthError, AuthError, AUTH_ERROR_CODES } from "@0-auth/zero-auth";

try {
  const payload = await auth.verifyToken(tokenString);
} catch (err) {
  if (isAuthError(err)) {
    console.error(`Auth failed [${err.code}]: ${err.message} (HTTP ${err.statusCode})`);
  }
}

Standard Error Codes

CodeStatus CodeDescription
AUTH_TOKEN_MISSING401No authentication token found in request headers or cookies.
AUTH_TOKEN_INVALID401Token is malformed or signature verification failed.
AUTH_TOKEN_EXPIRED401Token has exceeded its expiration time.
AUTH_UNAUTHORIZED401General unauthenticated error (e.g. invalid credentials or missing user state).
AUTH_FORBIDDEN403User is authenticated but does not possess the required role or permission.
AUTH_CSRF_INVALID403Auth cookies are present but the CSRF token is missing or invalid.

TypeScript Support

zero-auth automatically augments Express's Request type with req.user.

import type { AuthUser } from "@0-auth/zero-auth";

interface AuthUser {
  id: string;
  email?: string;
  role?: string;
  permissions?: string[];
  iat?: number;
  exp?: number;
  jti?: string;
  fid?: string; // Token family ID (when rotation is enabled)
  [key: string]: unknown; // Custom claims
}

Custom claims in your JWT payload are preserved across token refreshes and are typed on req.user:

// Sign with custom claims
await auth.generateAccessToken({
  id: "user_123",
  role: "admin",
  tenantId: "tenant_abc",
});

// In route handler
app.get("/tenant", auth.protect(), (req, res) => {
  const tenantId = req.user.tenantId; // accessible
  res.json({ tenantId });
});

API Reference

Instance Methods (auth.*)

Created via const auth = createAuth(config):

MethodReturn TypeDescription
generateAccessToken(payload)Promise<string>Generates a signed access token.
generateRefreshToken(payload)Promise<string>Generates a signed refresh token.
generateTokenPair(payload)Promise<{ accessToken, refreshToken }>Generates both access and refresh tokens in parallel.
verifyToken(token)Promise<AuthUser>Verifies access token signature and expiration.
verifyRefreshToken(token)Promise<AuthUser>Verifies refresh token signature and expiration.
decodeToken(token)AuthUserDecodes a token without verifying signature.
protect()RequestHandlerExpress middleware: rejects requests without valid access token (401).
authorize(roles)RequestHandlerExpress middleware: ensures req.user.role is in allowed roles (403).
authorizePermissions(permissions)RequestHandlerExpress middleware: requires every listed permission in req.user.permissions (403).
optional()RequestHandlerExpress middleware: sets req.user if valid token present, allows guests.
csrf()RequestHandlerExpress middleware: validates CSRF tokens on configured methods when auth cookies are present.
sendAuthTokens(res, payload)Promise<TokenPair>Sets access + refresh HTTP-only cookies on res and returns tokens.
clearAuth(res)voidClears access and refresh auth cookies from res.
csrfToken(res)stringSets and returns a client-readable signed CSRF token.
refreshHandler()RequestHandlerExpress route handler for refreshing access tokens.
rotateTokens(payload)Promise<TokenPair>Generates a new access + refresh pair (for custom rotation logic).
getJwks()Promise<{ keys: Record<string, unknown>[] }>Returns public key(s) formatted as a JSON Web Key Set (JWKS).
jwksHandler()RequestHandlerExpress route handler that serves the JSON Web Key Set at /.well-known/jwks.json.
errorHandlerErrorRequestHandlerExpress error middleware for handling AuthError responses.

Standalone Utilities

All core helpers are also exported directly for custom architectures:

import {
  // Token Helpers
  signToken,
  verifyToken,
  decodeToken,

  // Extractors
  extractToken,
  extractRefreshToken,

  // Cookie Utilities
  setCookie,
  clearCookie,
  parseCookieHeader,

  // Error Helpers
  AuthError,
  isAuthError,
  authErrorHandler,

  // In-Memory Revocation (Testing / Dev)
  createInMemoryRevocationStore,
  // Redis-backed revocation (distributed deployments)
  createRedisRevocationStore,
} from "@0-auth/zero-auth";
UtilityDescription
signToken(payload, secret, options)Low-level JWT signing function using jose.
verifyToken(token, secret)Low-level JWT verification function.
decodeToken(token)Unsafely decodes token payload without signature verification.
extractToken(req, cookieName?)Extracts access token from Authorization header or cookie.
extractRefreshToken(req, cookieName?)Extracts refresh token from cookie, body, or header.
setCookie(res, name, value, options)Sets an HTTP-only cookie with secure defaults.
clearCookie(res, name, options)Clears a cookie matching its path/domain/sameSite settings.
parseCookieHeader(cookieHeader)Zero-dependency cookie string parser.
createInMemoryRevocationStore()In-memory token revocation helper for development and tests.
createRedisRevocationStore(redis, ttlSeconds?)ioredis-compatible store for atomic rotation and family revocation.
createJwksResolver(jwksUri, options?)Creates a remote JWKS resolver function for low-level verifyToken.
exportPublicKeyToJwk(publicKey, options?)Converts SPKI PEM or KeyLike public key to a standard JWK object.

Production & Security Best Practices

  1. Secret Keys:
    • Always supply separate accessSecret and refreshSecret values with a minimum length of 32 characters.
    • Generate secure keys using:
      node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
      
  2. HTTPS & Cookies:
    • In production (NODE_ENV=production), secure: true is automatically enabled on cookies so tokens are only transmitted over HTTPS.
    • HTTP-only cookies prevent client-side JavaScript access, neutralizing XSS token theft.
    • Mount auth.csrf() for cookie-authenticated state-changing requests and send the token in the configured header.
  3. Token Lifespans:
    • Keep accessExpiresIn short (15m recommended).
    • Refresh tokens can have longer lifespans (7d - 30d).
  4. Token Revocation in Distributed Systems:
    • Use Redis or your primary database with TTLs to track revoked jti identifiers when refreshOptions.rotate: true is enabled.
  5. Abuse Prevention:
    • Rate-limit login, registration, refresh, and password-reset endpoints.
    • Use an allow-listed CORS policy for browser clients.
    • Monitor authentication failures, refresh replays, and revocation-store errors.

Compatibility

EnvironmentSupportedNotes
Node.js>= 18.0.0Full ESM & CommonJS support
Express4.x & 5.xPeer dependency for middleware
Bun>= 1.0.0Native runtime support
TypeScript>= 5.0.0Declarations bundled
Serverless / Edge✅Powered by jose (Web Crypto API compliant)

The npm tarball includes the README, CHANGELOG, license, compiled ESM/CommonJS builds, and TypeScript declarations. The full guide and generated API site are built from the repository's docs/ source.


Contributing

Found a bug or have an idea? Open an issue or read the contribution guide.


License

MIT © zero-auth contributors

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 11

Collected releases
VersionPublishedDirect dependenciesOpen version
1.6.0Registry latest27 Sept 202615
1.5.014 Sept 202615
1.4.14 Sept 202615
1.4.04 Sept 202615
1.3.03 Sept 202618
1.2.029 Aug 202618
1.1.325 Aug 202618
1.1.224 Aug 202618
1.0.221 Aug 202618
1.0.120 Aug 202616
1.0.020 Aug 202616

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

PENDING
0-auth/zero-auth

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 21:53 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:a4d411f9e361dedbd741154463741d73d3641f6efa1dd4f53456353d3c221ec9