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

@hintergrund/auth

v0.1.2

Published

Lightweight authentication library for Cloudflare Workers with password hashing, JWT tokens, and cookie-based sessions

Readme

@hintergrund/auth

Lightweight authentication library for Cloudflare Workers and edge runtimes, featuring secure password hashing, JWT tokens, and session-based authentication.

Features

  • Password Hashing: PBKDF2-SHA256 with salt and secret (pepper)
  • JWT Tokens: Stateless authentication with HMAC-SHA256
  • Session Tokens: Stateful authentication with KV/database storage
  • Cookie Management: Secure, HttpOnly cookie handling
  • Security First: Constant-time comparisons, secure defaults
  • Lightweight: Zero dependencies, uses Web Crypto API
  • Edge Ready: Built for Cloudflare Workers and edge runtimes

Installation

npm install @hintergrund/auth

Quick Start

JWT Authentication (Stateless)

import { hashPassword, login, authMiddleware } from '@hintergrund/auth';

const secret = 'your-secret-key';

// Register: Hash password for storage
const hash = await hashPassword('user-password', secret);

// Login: Verify password and return JWT cookie
const response = await login('user-password', hash, secret, {
  exp: 60 * 60 * 24, // 24 hours
  payload: { userId: '123', role: 'admin' }
});

// Protect routes: Verify JWT token
const auth = await authMiddleware(request, secret);
if (!auth.authenticated) {
  return new Response('Unauthorized', { status: 401 });
}
console.log(auth.payload); // { userId: '123', role: 'admin', ... }

Session Authentication (Stateful)

import {
  hashPassword,
  sessionLogin,
  sessionMiddleware,
  createKVSessionStore
} from '@hintergrund/auth';

const secret = 'your-secret-key';
const store = createKVSessionStore(env.SESSIONS);

// Register: Hash password for storage
const hash = await hashPassword('user-password', secret);

// Login: Verify password and create session in KV
const response = await sessionLogin('user-password', hash, secret, store, {
  exp: 60 * 60 * 24, // 24 hours
  data: { userId: '123', role: 'admin' }
});

// Protect routes: Verify session token
const auth = await sessionMiddleware(request, store);
if (!auth.authenticated) {
  return new Response('Unauthorized', { status: 401 });
}
console.log(auth.data); // { userId: '123', role: 'admin' }

JWT vs Session Authentication

| Feature | JWT (Stateless) | Session (Stateful) | |---------|-----------------|-------------------| | Storage | Token contains all data | Data stored in KV/DB | | Revocation | Cannot revoke until expiry | Instant revocation | | Scalability | No server state needed | Requires shared storage | | Token size | Larger (contains payload) | Small (just ID) | | Use case | APIs, microservices | Traditional web apps |


API Reference

Password Hashing

generateRandomSecret(length?)

Generates a cryptographically secure random secret.

import { generateRandomSecret } from '@hintergrund/auth';

const secret = generateRandomSecret(); // 32 bytes default
const longSecret = generateRandomSecret(64); // 64 bytes

Parameters:

  • length (number): Number of random bytes (default: 32)

Returns: string - Base64-encoded random secret


hashPassword(password, secret, options?)

Hashes a password using PBKDF2-SHA256 with salt and secret (pepper).

import { hashPassword } from '@hintergrund/auth';

const hash = await hashPassword('user-password', 'your-secret-key');

// With custom iterations
const hash = await hashPassword('user-password', 'your-secret-key', {
  iterations: 100000 // default: 50000
});

Parameters:

  • password (string): Plain text password
  • secret (string): Secret key (pepper) for additional security
  • options (HashOptions): Optional configuration

HashOptions:

  • iterations (number): PBKDF2 iterations (default: 50000)

Returns: Promise<string> - Base64-encoded hash (includes iterations + salt + hash)


verifyPassword(password, secret, storedHash)

Verifies a password against a stored hash using constant-time comparison.

import { verifyPassword } from '@hintergrund/auth';

const isValid = await verifyPassword('user-password', 'your-secret-key', storedHash);

Parameters:

  • password (string): Plain text password to verify
  • secret (string): Secret key used during hashing
  • storedHash (string): Base64-encoded hash from storage

Returns: Promise<boolean> - True if password matches


JWT Functions

signJwt(secret, exp?, payload?)

Signs a JWT token with the given secret and payload.

import { signJwt } from '@hintergrund/auth';

const token = await signJwt('your-secret-key', 60 * 60 * 24, {
  userId: '123',
  role: 'admin'
});

Parameters:

  • secret (string): Secret key for signing
  • exp (number): Expiration time in seconds (default: 86400)
  • payload (object): Additional payload data (default: {})

Returns: Promise<string> - Signed JWT token


verifyJwt(token, secret)

Verifies a JWT token's signature and expiration.

import { verifyJwt } from '@hintergrund/auth';

const isValid = await verifyJwt(token, 'your-secret-key');

Parameters:

  • token (string): JWT token string
  • secret (string): Secret key used for signing

Returns: Promise<boolean> - True if token is valid and not expired


decodeJwt(token)

Decodes a JWT token without verifying its signature.

Warning: Always verify the token with verifyJwt() before trusting the payload!

import { decodeJwt } from '@hintergrund/auth';

const { header, payload } = decodeJwt(token);

Parameters:

  • token (string): JWT token string

Returns: DecodedJwt - Object with header and payload properties


JWT Authentication

login(password, storedHash, secret, options?)

Handles password-based login with JWT token generation.

import { login } from '@hintergrund/auth';

const response = await login('user-password', storedHash, 'your-secret-key', {
  redirect: 'dashboard',
  exp: 60 * 60 * 24 * 7, // 7 days
  payload: { userId: '123', email: '[email protected]' },
  cookieName: 'token',
  cookiePath: '/',
  cookieDomain: 'example.com',
  sameSite: 'Strict'
});

Parameters:

  • password (string): Plain text password provided by user
  • storedHash (string): Stored password hash from database
  • secret (string): Secret key for password verification and JWT signing
  • options (LoginOptions): Optional configuration

LoginOptions: | Option | Type | Default | Description | |--------|------|---------|-------------| | redirect | string | - | Redirect path after login | | exp | number | 86400 | Token expiration in seconds | | payload | object | {} | Additional JWT payload data | | cookieName | string | 'token' | Cookie name | | cookiePath | string | '/' | Cookie path | | cookieDomain | string | - | Cookie domain | | sameSite | 'Strict' | 'Lax' | 'None' | 'Strict' | SameSite attribute |

Returns: Promise<Response> - Response with Set-Cookie header or 401 error


logout(options?)

Handles user logout by clearing the auth cookie.

import { logout } from '@hintergrund/auth';

const response = logout({
  redirect: '/login',
  cookieName: 'token'
});

LogoutOptions: | Option | Type | Default | Description | |--------|------|---------|-------------| | redirect | string | '/' | Redirect path after logout | | cookieName | string | 'token' | Cookie name | | cookiePath | string | '/' | Cookie path | | cookieDomain | string | - | Cookie domain |

Returns: Response - Response with cleared cookie


authMiddleware(request, secret, options?)

Middleware function to verify JWT token from cookie.

import { authMiddleware } from '@hintergrund/auth';

const auth = await authMiddleware(request, 'your-secret-key', {
  cookieName: 'token'
});

if (auth.authenticated) {
  console.log('User:', auth.payload);
}

MiddlewareOptions: | Option | Type | Default | Description | |--------|------|---------|-------------| | cookieName | string | 'token' | Cookie name to read token from |

Returns: Promise<AuthResult>

AuthResult:

  • authenticated (boolean): Whether authentication was successful
  • payload (JwtPayload): Decoded JWT payload if authenticated

Session Authentication

generateSessionToken()

Generates a cryptographically secure session token.

import { generateSessionToken } from '@hintergrund/auth';

const token = generateSessionToken(); // URL-safe base64, 32 bytes

Returns: string - URL-safe base64 session token


createKVSessionStore(kv, prefix?)

Creates a session store backed by Cloudflare KV.

import { createKVSessionStore } from '@hintergrund/auth';

const store = createKVSessionStore(env.SESSIONS, 'session:');

Parameters:

  • kv (KVNamespace): Cloudflare KV namespace
  • prefix (string): Key prefix for sessions (default: 'session:')

Returns: SessionStore - Session store implementation


sessionLogin(password, storedHash, secret, store, options?)

Handles password-based login with session token generation.

import { sessionLogin, createKVSessionStore } from '@hintergrund/auth';

const store = createKVSessionStore(env.SESSIONS);

const response = await sessionLogin(
  'user-password',
  storedHash,
  'your-secret-key',
  store,
  {
    exp: 60 * 60 * 24, // 24 hours
    data: { userId: '123', role: 'admin' },
    cookieName: 'session',
    sameSite: 'Strict'
  }
);

SessionLoginOptions: | Option | Type | Default | Description | |--------|------|---------|-------------| | redirect | string | - | Redirect path after login | | exp | number | 86400 | Session expiration in seconds | | data | SessionData | {} | Session data to store | | cookieName | string | 'session' | Cookie name | | cookiePath | string | '/' | Cookie path | | cookieDomain | string | - | Cookie domain | | sameSite | 'Strict' | 'Lax' | 'None' | 'Strict' | SameSite attribute |

Returns: Promise<Response> - Response with Set-Cookie header or 401 error


sessionLogout(request, store, options?)

Handles logout by clearing the session from storage and cookie.

import { sessionLogout, createKVSessionStore } from '@hintergrund/auth';

const store = createKVSessionStore(env.SESSIONS);
const response = await sessionLogout(request, store, {
  redirect: '/login'
});

SessionLogoutOptions: | Option | Type | Default | Description | |--------|------|---------|-------------| | redirect | string | '/' | Redirect path after logout | | cookieName | string | 'session' | Cookie name | | cookiePath | string | '/' | Cookie path | | cookieDomain | string | - | Cookie domain |

Returns: Promise<Response> - Response with cleared cookie


sessionMiddleware(request, store, options?)

Middleware function to verify session token from cookie.

import { sessionMiddleware, createKVSessionStore } from '@hintergrund/auth';

const store = createKVSessionStore(env.SESSIONS);
const auth = await sessionMiddleware(request, store, {
  cookieName: 'session'
});

if (auth.authenticated) {
  console.log('Session data:', auth.data);
  console.log('Session token:', auth.token);
}

SessionMiddlewareOptions: | Option | Type | Default | Description | |--------|------|---------|-------------| | cookieName | string | 'session' | Cookie name to read token from |

Returns: Promise<SessionAuthResult>

SessionAuthResult:

  • authenticated (boolean): Whether authentication was successful
  • data (SessionData): Session data if authenticated
  • token (string): Session token if authenticated

Custom Session Store

Implement the SessionStore interface for custom storage (database, Redis, etc.):

import type { SessionStore, SessionData } from '@hintergrund/auth';

const customStore: SessionStore = {
  async get(token: string): Promise<SessionData | null> {
    // Fetch from your database
    const row = await db.query('SELECT data FROM sessions WHERE token = ?', [token]);
    return row ? JSON.parse(row.data) : null;
  },

  async set(token: string, data: SessionData, ttl?: number): Promise<void> {
    const expiresAt = ttl ? Date.now() + ttl * 1000 : null;
    await db.query(
      'INSERT INTO sessions (token, data, expires_at) VALUES (?, ?, ?)',
      [token, JSON.stringify(data), expiresAt]
    );
  },

  async delete(token: string): Promise<void> {
    await db.query('DELETE FROM sessions WHERE token = ?', [token]);
  }
};

Utility Functions

readRequestBody(request)

Parses request body from JSON or form data.

import { readRequestBody } from '@hintergrund/auth';

const body = await readRequestBody(request);
// body.email, body.password, etc.

Supported Content-Types:

  • application/json
  • application/x-www-form-urlencoded
  • multipart/form-data

Returns: Promise<Record<string, unknown>>

Throws: Error if Content-Type is not supported


readCookie(request, name)

Reads a cookie value from request headers.

import { readCookie } from '@hintergrund/auth';

const token = readCookie(request, 'session');

Returns: string | null - Cookie value or null if not found


Examples

Complete working examples for different storage configurations:

| Example | User Storage | Session Storage | Use Case | |---------|--------------|-----------------|----------| | kv-only | KV | KV | Simple apps, prototypes | | d1-only | D1 | D1 | Relational data, complex queries | | d1-with-kv-sessions | D1 | KV | Best of both: relational users, fast sessions |

Quick Start

cd examples/kv-only
npm install
npm run dev

Which Example to Use?

  • kv-only: Simplest setup, good for small apps and prototypes
  • d1-only: Need relational queries, foreign keys, or complex user data
  • d1-with-kv-sessions: Production apps needing both relational data and fast session lookups

TypeScript Support

Full type definitions included:

import type {
  // JWT types
  JwtPayload,
  JwtHeader,
  DecodedJwt,
  // JWT auth types
  LoginOptions,
  LogoutOptions,
  MiddlewareOptions,
  AuthResult,
  // Session types
  SessionStore,
  SessionData,
  SessionLoginOptions,
  SessionLogoutOptions,
  SessionMiddlewareOptions,
  SessionAuthResult,
  // Crypto types
  HashOptions,
} from '@hintergrund/auth';

Security Considerations

Password Hashing

  • Uses PBKDF2-SHA256 with 50,000 iterations (configurable)
  • Random 16-byte salt per password
  • Secret (pepper) adds additional security layer
  • Constant-time comparison prevents timing attacks

JWT Security

  • Signature verified before checking expiration (prevents timing attacks)
  • HMAC-SHA256 signing algorithm
  • Secure cookie defaults (HttpOnly, Secure, SameSite=Strict)

Session Security

  • 256-bit cryptographically random tokens
  • Server-side storage prevents token tampering
  • Immediate revocation on logout
  • Automatic expiration via KV TTL

Production Checklist

  • Use strong, randomly generated secrets (generateRandomSecret())
  • Store secrets in environment variables
  • Use HTTPS (Cloudflare handles this)
  • Implement rate limiting for auth endpoints
  • Set appropriate expiration times
  • Consider CSRF protection for web apps
  • Regularly rotate secrets

License

MIT

Author

hintergrund.dev