Sitelet https://github.com/awesome-lang-auth/awesome-node-auth
Skip to content

Repository files navigation

awesome-node-auth

npm version license github stars

NPM

A production-ready, database-agnostic JWT authentication library for Node.js written in TypeScript. Drop-in auth for Express, NestJS, Next.js, Fastify and any other Node.js framework — connect to any database through a single interface.

The self-hosted alternative to Supertokens and Supabase Auth. Same enterprise-grade features, zero vendor lock-in.


Installation

npm install @awesome-lang-auth/node

Up to 1.10.0 the package was published as awesome-node-auth. To migrate, replace the dependency with @awesome-lang-auth/node and change your imports from 'awesome-node-auth' to '@awesome-lang-auth/node'; the API is the same.

Quick Start

import express from 'express';
import { AuthConfigurator, AuthEventBus } from '@awesome-lang-auth/node';
import { myUserStore } from './my-user-store'; // your IUserStore impl

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

const eventBus = new AuthEventBus();

const auth = new AuthConfigurator(
  {
    accessTokenSecret: process.env.ACCESS_TOKEN_SECRET!,
    refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET!,
    accessTokenExpiresIn: '15m',
    refreshTokenExpiresIn: '7d',
  },
  myUserStore,
  { eventBus },
);

app.use(auth.buildAllRouters({
  admin: {
    accessPolicy: 'is-admin-flag', // admin panel for users with isAdmin: true
  },
})); // mounts /auth/* and /auth/admin/*

// Grant the admin panel from a seed script or CLI task (sets isAdmin; needs IUserStore.update):
//   await auth.promoteToAdmin(userId, { method: 'flag' });

app.get('/protected', auth.middleware(), (req, res) => {
  res.json({ user: req.user });
});

app.listen(3000);

Implement IUserStore once for your database and you're done.
Full DB examples (MongoDB, PostgreSQL, MySQL, in-memory) → README.detailed.md.


Features

Area Highlights
Auth strategies Email/password · OAuth 2.0 (Google, GitHub, custom with return_path validation & HMAC state binding) · Magic links · SMS OTP · TOTP 2FA
Token management HttpOnly-cookie or Bearer mode · automatic access/refresh rotation · __Host-/__Secure- cookie prefixes
Identity Provider (IdP) mode (v1.9) RS256-signed JWTs · public JWKS endpoint (/.well-known/jwks.json) · Resource Server middleware · zero new dependencies
Stateful sessions (v1.5) ISessionStore + real-time revocation (checkOn: allcalls|refresh|none) · works behind your own L1/L2 cache layers
Dynamic email templates (v1.6) ITemplateStore — per-language mail templates + UI i18n with safe hardcoded fallback · built-in MemoryTemplateStore
CSRF protection Double-submit cookie pattern · __Host- prefix hardening against cookie-tossing
Account management Registration · change email/password · account deletion (onBeforeDeleteUser hook) · email verification (none/lazy/strict + server API/admin trigger)
Account linking Link multiple OAuth providers · conflict resolution via IPendingLinkStore
RBAC IRolesPermissionsStore with tenant awareness
Multi-tenancy ITenantStore for isolated tenant apps
Admin panel Full-featured admin UI: user management, sessions, roles, tenants, metadata, API keys, webhooks
Built-in UI Zero-dependency HTML/CSS/JS login UI served at <apiPrefix>/ui/ · headless mode for SPAs
Client libraries Angular · Flutter · React · served auth.js — see Ecosystem
Event-driven AuthEventBus · SSE push · inbound/outbound webhooks · telemetry
API keys M2M bcrypt-hashed keys with scopes, expiry, IP allowlist and audit log
OpenAPI / Swagger Auto-generated specs for auth, admin and tools routers

Identity Provider (IdP) Mode (v1.9)

Turn any Provisioner into a central IdP that issues RS256-signed JWTs. Downstream Resource Servers validate tokens via the public JWKS endpoint — no shared secrets.

Generate the RSA private key for your .env (uses Node.js built-in crypto — no install needed):

node -e "
const { generateKeyPairSync } = require('crypto');
const { privateKey } = generateKeyPairSync('rsa', { modulusLength: 2048 });
const pem = privateKey.export({ type: 'pkcs8', format: 'pem' });
console.log('IDP_PRIVATE_KEY=' + Buffer.from(pem).toString('base64'));
"

Then in your config:

// Provisioner (IdP)
const auth = new AuthConfigurator({
  accessTokenSecret: '...',
  refreshTokenSecret: '...',
  idProvider: {
    enabled: true,
    issuer: 'https://auth.myplatform.com',
    privateKey: Buffer.from(process.env.IDP_PRIVATE_KEY!, 'base64').toString('utf8'),
  },
}, userStore);

// Resource Server (downstream)
import { createJwksAuthMiddleware } from '@awesome-lang-auth/node';

app.use('/api', createJwksAuthMiddleware({
  jwksUrl: 'https://auth.myplatform.com/.well-known/jwks.json',
  issuer:  'https://auth.myplatform.com',
}), myApiRouter);

In development, if you omit privateKey an ephemeral keypair is auto-generated at startup.

→ Full guide: awesomenodeauth.com/docs/advanced/idp-mode


Key Endpoints

POST   /auth/login              POST /auth/refresh           GET  /auth/me
POST   /auth/register           POST /auth/logout            GET  /auth/sessions        ← device list
POST   /auth/forgot-password    POST /auth/change-password   DELETE /auth/sessions/:h   ← revoke device
POST   /auth/magic-link/send    POST /auth/2fa/verify        DELETE /auth/account
GET    /auth/oauth/:provider    GET  /auth/oauth/:provider/callback
POST   /auth/sessions/cleanup   POST /auth/add-phone         PATCH /auth/profile
GET    /.well-known/jwks.json                                                            ← IdP mode only

Optional Stores Snapshot

const auth = new AuthConfigurator(
  { ...config, templateStore }, // ITemplateStore — dynamic email templates + UI i18n (v1.6), part of AuthConfig
  userStore,
  { eventBus },                 // optional; the third argument only accepts { eventBus }
);

app.use('/auth', auth.router({
  sessionStore,        // ISessionStore        — stateful sessions + device management
  metadataStore,       // IUserMetadataStore   — arbitrary per-user key/value pairs
  rbacStore,           // IRolesPermissionsStore
  tenantStore,         // ITenantStore
  linkedAccountsStore, // ILinkedAccountsStore — several OAuth providers per user
  pendingLinkStore,    // IPendingLinkStore    — OAuth account-linking conflicts (with linkedAccountsStore)
}));

app.get('/protected', auth.middleware(), handler); // uses the sessionStore passed to router() above (call router() first)

With buildAllRouters(), pass the same stores as auth: { … }; the admin panel takes its own in admin: { … } (see Admin UI).

Full configuration reference → README.detailed.md § Configuration


Cookie Management

Clear auth cookies on demand with TokenService.prototype.clearTokenCookies (there is no standalone clearAuthCookies export):

import { TokenService } from '@awesome-lang-auth/node';

new TokenService().clearTokenCookies(res, authConfig);

Admin UI

Use auth.buildAllRouters({ admin: ... }) to mount both the main auth router and the admin router together. The admin router lives at /auth/admin/*, and jwtSecret is auto-filled from AuthConfig.accessTokenSecret. Set accessPolicy (or a non-empty legacy adminSecret): without either, the admin routes are mounted unprotected and a WARNING is written to stderr. Without accessPolicy, an adminSecret that is present but empty (an unset environment variable, for example) throws a configuration error at startup.

admin option (AdminOptions) Unlocks
sessionStore Sessions tab
rbacStore Roles & Permissions tab
tenantStore Tenants tab
userMetadataStore Metadata section in user detail
settingsStore ⚙️ Control tab
linkedAccountsStore Linked Accounts column
apiKeyStore 🔑 API Keys tab
webhookStore 🔗 Webhooks tab
templateStore Email & UI tab
uploadDir (optionally uploadBaseUrl) Logo upload in branding

Two login endpoints, two audiences

  • /auth/ui/login — end-user login for your application (built-in UI, ui: { enabled: true })
  • /auth/admin/ — admin panel for operators (its sign-in form posts to /auth/admin/login)

They are intentionally different flows. If you mount the admin UI for operators, keep linking end users to /auth/ui/login.

The admin sign-in form checks the password only, with no second factor, and its session opens the admin console and nothing else. To send operators through the application login and its 2FA flow, set admin.loginPath: '/auth/ui/login' (after signing in they land on /; reopen /auth/admin/). POST /auth/admin/login stays mounted and still accepts the password alone, so block it at your proxy if operators must always pass 2FA.


Ecosystem

awesome-node-auth is the reference server of a family of libraries that port its HTTP API to other runtimes, plus client libraries for that API.

Servers

Runtime Repository Status
Node.js awesome-node-auth (this repo) npm @awesome-lang-auth/node
Go awesome-go-auth Go module, 0.11.x · 1.0 in progress
AWS Lambda awesome-lambda-auth Preview
Python awesome-python-auth PyPI awesome-python-auth 1.1.0
Rust awesome-rust-auth Git only (not on crates.io)
Dart awesome-dart-auth Git only (not on pub.dev)

Clients

Client Package Status
Angular ng-awesome-node-auth npm · to be renamed @awesome-lang-auth/angular
Flutter awesome_node_auth_flutter pub.dev · to be renamed awesome_flutter_auth
React @awesome-lang-auth/react npm 0.1.0
Browser auth.js Served by this library at <apiPrefix>/ui/auth.js when ui.enabled is set — see Including auth.js

Documentation

Resource Link
Full reference README.detailed.md
Wiki / Guides awesomenodeauth.com
Changelog CHANGELOG.md
Demo apps demo/
Framework examples examples/

The companion MCP server (awesome-node-auth-mcp-server) has been retired and is no longer available.


License

MIT · © 2026 nik2208 · Sponsor ❤

About

The Sovereign Identity & Communication Layer for Node.js. Scalable by design. No SaaS/DB lock-in. MCP Server for auto-config in IDE, Hybrid Web/Mobile Auth, Dynamic in/out Webhooks via decorators, JWT/Api-Key, custom JWT Payload, RBAC/Metadata, Admin UI, served full-featured customizable Auth UI, SSE/tracking via AuthEventBus

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

5 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages