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

@gablura/auth-core

v1.2.4

Published

Production-ready authentication core for Express.js backends. Dual-token RS256 JWT architecture with session binding, token rotation, 2FA, account lockout, and audit logging.

Readme

@gablura/auth-core

Production-ready authentication engine for Node.js backends.

Features

  • Dual-token RS256 JWT architecture (15min access / 7d refresh)
  • Token exchange — HMAC-SHA256 signed proof between services
  • Refresh token rotation — Atomic Lua script, distributed lock
  • Session binding — Device fingerprint + IP validation
  • Session management — Max concurrent sessions, eviction
  • Account lockout — Configurable failure threshold
  • 2FA (TOTP) — Generate and verify time-based one-time passwords
  • Audit logging — 50+ event types with severity levels
  • CSRF protection — Redis-backed tokens
  • Rate limiting — Sliding window, configurable per-route
  • Cache layer — Auth result + user profile caching

Installation

npm install @gablura/auth-core ioredis

Quick Start

import { AuthService } from "@gablura/auth-core";
import Redis from "ioredis";
import fs from "fs";

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

const auth = new AuthService({
  redis,
  userStore: {
    findById: (id) => prisma.user.findUnique({ where: { id } }),
    findByEmail: (email) => prisma.user.findUnique({ where: { email } }),
    update: (id, data) => prisma.user.update({ where: { id }, data }),
    updateEmailVerified: (id, date) => prisma.user.update({ where: { id }, data: { emailVerified: date } }),
  },
  hmacSecret: process.env.AUTH_SECRET!,
  jwt: {
    privateKey: fs.readFileSync("keys/private.pem", "utf8"),
    publicKey: fs.readFileSync("keys/public.pem", "utf8"),
  },
});

High-Level API

Token Exchange

Exchange a HMAC-signed proof for JWT tokens:

const tokens = await auth.exchange({
  userId: user.id,
  email: user.email,
  role: user.role,
  sessionId: crypto.randomUUID(),
  timestamp: Date.now(),
  signature: hmacSignature,
});

// tokens.accessToken, tokens.refreshToken, tokens.sseToken, tokens.sessionId

Verify Token

Verify an access token and load the user:

const { user, payload } = await auth.verifyToken({
  token: accessToken,
  ipAddress: "192.168.1.1",
  userAgent: "Mozilla/5.0...",
});

// payload.id, payload.email, payload.role, payload.jti, payload.sessionId

Refresh Tokens

Rotate refresh tokens atomically:

const newTokens = await auth.refresh({
  refreshToken: currentRefreshToken,
});

// newTokens.accessToken, newTokens.refreshToken, newTokens.sseToken

Logout

// Single session
await auth.logout({
  userId: user.id,
  sessionId: sessionId,
  accessTokenJti: jti,
  accessToken: token,
});

// All sessions
await auth.logout({
  userId: user.id,
  sessionId: sessionId,
  logoutAll: true,
});

Two-Factor Authentication

// Setup — generate secret + URI for QR code
const { secret, uri } = auth.generateTwoFactor();
// Display uri as QR code to user, store secret in database

// Verify TOTP code
const valid = await auth.verifyTwoFactor({ token: "123456", secret });

Session Management

// List active sessions
const sessions = await auth.getActiveSessions(userId);

// Revoke a specific session (clears auth result cache)
await auth.revokeSession(userId, sessionId, accessTokenJti);

Account Lockout

// Record failed login attempt
const result = await auth.recordLoginFailure(email);
if (result.locked) {
  console.log(`Account locked until ${result.unlocksAt}`);
}

// Clear failures on successful login
await auth.clearLoginFailures(email);

// Check if account is locked
const status = await auth.isAccountLocked(email);

Audit Logging

auth.log("WORKSPACE_CREATED", { userId, workspaceId });

Express Integration

For Express.js applications, use MiddlewareFactory for HTTP middleware:

import { MiddlewareFactory } from "@gablura/auth-core";

const factory = new MiddlewareFactory(config);

// Auth routes
app.post("/api/v1/auth/exchange", factory.createExchangeHandler());
app.post("/api/v1/auth/refresh", factory.createRefreshHandler());
app.post("/api/v1/auth/logout", factory.createLogoutHandler());

// Protected routes
app.get("/api/v1/profile",
  factory.createAuthenticateMiddleware(),
  (req, res) => { res.json({ user: req.user }); }
);

// Role-based access
app.get("/api/v1/admin",
  factory.createAuthenticateMiddleware(),
  factory.createAuthorizeMiddleware("ADMIN"),
  (req, res) => { res.json({ admin: true }); }
);

Adapters

RedisAdapter

Works with ioredis or any compatible client:

import Redis from "ioredis";
const redis = new Redis(process.env.REDIS_URL);

const auth = new AuthService({ redis, ... });

UserStore

Implement this to connect your database:

const userStore = {
  findById: async (id) => db.user.findUnique({ where: { id } }),
  findByEmail: async (email) => db.user.findUnique({ where: { email } }),
  update: async (id, data) => db.user.update({ where: { id }, data }),
  updateEmailVerified: async (id, date) => db.user.update({ where: { id }, data: { emailVerified: date } }),
};

CacheAdapter

Optional but recommended for performance:

const cache = {
  get: async (key) => { const v = await redis.get(key); return v ? JSON.parse(v) : null; },
  set: async (key, val, ttl) => { await redis.setex(key, ttl!, JSON.stringify(val)); },
  delete: async (key) => { await redis.del(key); },
};

Configuration

interface AuthCoreConfig {
  redis: RedisAdapter;           // Required
  userStore: UserStore;          // Required
  hmacSecret: string;            // Required — shared secret for token exchange
  jwt: TokenConfig;              // Required — RSA key pair
  cache?: CacheAdapter;          // Optional — enables auth result caching
  auditLogger?: AuditLogger;     // Optional — persists audit events
  observability?: ObservabilitySink; // Optional — Sentry/Datadog integration
  errors?: ErrorFactory;         // Optional — custom error classes
  keyPrefix?: string;            // Default: "focura:"
  lockout?: LockoutConfig;       // Default: 10 failures / 15min lock / 1hr window
  session?: SessionConfig;       // Default: 7d inactivity / 7d absolute / 5 max concurrent
  serverSecret?: string;         // Optional — HMAC secret for server-to-server binding bypass
  trustedProxies?: string[];     // Optional — CIDR-aware proxy IP validation for X-Forwarded-For
}

Generated RSA Keys

openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -in private.pem -pubout -out public.pem

API Hierarchy

Primary API (Recommended)

import { AuthService } from "@gablura/auth-core";

The AuthService class provides high-level, framework-agnostic authentication operations.

Extension API

Interfaces for integrating your infrastructure:

import type { UserStore, RedisAdapter, CacheAdapter, AuditLogger } from "@gablura/auth-core";

Advanced API

Low-level classes for custom integrations:

import { TokenManager, SessionManager, TotpManager, AccountLockout } from "@gablura/auth-core";

These are available but not required for normal application development.


Default Configuration

import { DEFAULTS } from "@gablura/auth-core";

// All default values:
DEFAULTS.keyPrefix;              // "focura:"
DEFAULTS.issuer;                 // "focura-app"
DEFAULTS.audience;               // "focura-backend"
DEFAULTS.accessTokenExpiry;      // "15m"
DEFAULTS.refreshTokenExpiry;     // "7d"
DEFAULTS.sseTokenExpiry;         // "30s"
DEFAULTS.maxConcurrentSessions;  // 5
DEFAULTS.lockoutMaxFailures;     // 10
DEFAULTS.lockoutSeconds;         // 900 (15 minutes)
DEFAULTS.lockoutWindowSeconds;   // 3600 (1 hour)
DEFAULTS.inactivityTimeout;      // 604800 (7 days)
DEFAULTS.absoluteTimeout;        // 604800 (7 days)
DEFAULTS.maxSessionAge;          // 2592000000 (30 days)

resolveConfig

Merges your config with defaults. Useful for advanced customization:

import { resolveConfig } from "@gablura/auth-core";

const resolved = resolveConfig({
  redis,
  userStore,
  hmacSecret: "...",
  jwt: { privateKey: "...", publicKey: "..." },
  lockout: { maxFailures: 5 },  // override default 10
});

// resolved contains all values with defaults applied

Error Handling

All errors include code and statusCode for API responses:

import {
  UnauthorizedError,      // 401, code: UNAUTHORIZED
  TokenExpiredError,      // 401, code: TOKEN_EXPIRED
  InvalidTokenError,      // 401, code: INVALID_TOKEN
  TokenRevokedError,      // 401, code: TOKEN_REVOKED
  SessionHijackError,     // 401, code: SESSION_HIJACK_DETECTED
  EmailNotVerifiedError,  // 403, code: EMAIL_NOT_VERIFIED
  AccountBannedError,     // 403, code: ACCOUNT_BANNED
  ForbiddenError,         // 403, code: FORBIDDEN
  BadRequestError,        // 400, code: BAD_REQUEST
  ValidationError,        // 400, code: VALIDATION_ERROR
} from "@gablura/auth-core";

Example: Catching errors

try {
  const { user, payload } = await auth.verifyToken({ token, ipAddress, userAgent });
} catch (e) {
  if (e instanceof TokenExpiredError) {
    return res.status(401).json({ error: "Token expired", code: e.code });
  }
  if (e instanceof InvalidTokenError) {
    return res.status(401).json({ error: "Invalid token", code: e.code });
  }
  if (e instanceof SessionHijackError) {
    return res.status(401).json({ error: "Session hijack detected", code: e.code });
  }
  if (e instanceof AccountBannedError) {
    return res.status(403).json({ error: "Account banned", reason: e.message });
  }
  if (e instanceof ValidationError) {
    return res.status(400).json({ error: e.message, details: e.details });
  }
  return res.status(500).json({ error: "Internal server error" });
}

defaultErrors Factory

Use as base for custom error classes:

import { defaultErrors } from "@gablura/auth-core";

const customErrors = {
  ...defaultErrors,
  UnauthorizedError: (msg) => new MyCustomUnauthorizedError(msg),
};

const auth = new AuthService({ ..., errors: customErrors });

Input & Result Types

All types are exported for TypeScript autocompletion:

import type {
  // Token exchange
  ExchangeInput,        // { userId, email, role, sessionId, timestamp, signature }
  ExchangeResult,       // { accessToken, refreshToken, sseToken, sessionId }

  // Token verification
  VerifyTokenInput,     // { token, ipAddress?, userAgent? }
  VerifyTokenResult,    // { user, payload }

  // Refresh
  RefreshInput,         // { refreshToken }
  AuthRefreshResult,    // { accessToken, refreshToken, sseToken }

  // Logout
  LogoutInput,          // { userId, sessionId, accessTokenJti?, accessToken?, logoutAll? }

  // 2FA
  TwoFactorSetupResult, // { secret, uri }
  TwoFactorVerifyInput, // { token, secret }

  // Tokens
  TokenPayload,         // { id, email, role, type, version, jti, sessionId? }
  TokenPair,            // { accessToken, refreshToken, accessTokenExpiry, refreshTokenExpiry }

  // Config sub-types
  TokenConfig,          // { privateKey, publicKey, issuer?, audience?, accessTokenExpiry?, ... }
  SessionConfig,        // { inactivityTimeout?, absoluteTimeout?, maxConcurrent?, metadataTtl? }
  LockoutConfig,        // { maxFailures?, lockoutSeconds?, windowSeconds? }

  // Adapters
  RedisAdapter,
  RedisPipeline,
  UserStore,
  User,
  CacheAdapter,
  AuditLogger,
  ObservabilitySink,
  ErrorFactory,
  ZodSchema,

  // Session
  SessionMetadata,      // { deviceId, ipAddress, userAgent, location?, lastActivity }
  SessionLifecycle,     // { recordCreation, invalidate, isTracked, isInactive }
  DeviceFingerprint,    // { userAgent, acceptLanguage, acceptEncoding, ipAddress }
  AuthRequest,          // Extended request with user property
} from "@gablura/auth-core";

MiddlewareFactory Methods

All HTTP middleware and route handlers:

import { MiddlewareFactory } from "@gablura/auth-core";

const factory = new MiddlewareFactory(config);

// Route handlers
factory.createExchangeHandler();       // POST /api/v1/auth/exchange
factory.createRefreshHandler();        // POST /api/v1/auth/refresh
factory.createLogoutHandler();         // POST /api/v1/auth/logout

// Auth middleware
factory.createAuthenticateMiddleware();           // Verifies JWT, attaches req.user
factory.createAuthorizeMiddleware("ADMIN", "MOD"); // Role-based access

// Security middleware
factory.createCsrfMiddleware();                   // CSRF token validation
factory.createRateLimitMiddleware();              // Sliding window rate limiting
factory.createSessionTimeoutMiddleware();         // Inactivity + absolute timeout

Utility Functions

Low-level helpers available for advanced use:

import {
  generateDeviceFingerprint,  // (req) => DeviceFingerprint
  getClientIp,                // (req) => string
  isPrivateIp,                // (ip) => boolean — checks 10.x, 172.16-31.x, 192.168.x, 127.x
  normalizeUserAgent,         // (ua) => string — truncates long user agents
  createSessionMetadata,      // (fingerprint, sessionId) => SessionMetadata
  validateSessionBinding,     // (metadata, fingerprint, options) => { bound, reason? }
  looksLikeServerToServerRequest, // (req) => boolean
  looksLikeServerToServerUA,      // (ua) => boolean
} from "@gablura/auth-core";

Audit Event Types

50+ event types with severity levels:

import { AUDIT_SEVERITY } from "@gablura/auth-core";

// AUDIT_SEVERITY maps event types to severity:
// "info"    — LOGIN_SUCCESS, LOGOUT, TOKEN_REFRESHED, SESSION_BOUND, etc.
// "warn"    — LOGIN_FAILED, DEVICE_MISMATCH, RATE_LIMIT_EXCEEDED, etc.
// "critical" — TOKEN_REPLAY_DETECTED, ACCOUNT_LOCKED, MALWARE_DETECTED, etc.
import type { AuditEventType, AuditSeverity } from "@gablura/auth-core";

// AuditEventType — union of all 50+ event strings
// AuditSeverity — "info" | "warn" | "critical"

Zod Schemas

Validation schemas used internally (available for external use):

import { exchangeSchema, refreshSchema, logoutSchema } from "@gablura/auth-core";

// exchangeSchema validates ExchangeInput
// refreshSchema validates RefreshInput
// logoutSchema validates LogoutInput

const result = exchangeSchema.safeParse(data);
if (!result.success) {
  console.log(result.error.issues);
}

License

MIT