Secure Auth is a framework-agnostic authentication system for Node.js applications. It provides secure user authentication, session management, and WebAuthn support, all while remaining database-agnostic.
- 🔒 Secure Authentication using Argon2 password hashing and Ed25519 JWT signing
- 📌 Session-Based JWTs: Tokens tied to a session ID stored in the database
- 🛡 Automatic Session Validation: JWTs are valid only if the session exists in the DB
- 🌍 Framework-Agnostic: Works with Express, Fastify, Koa, or any other Node.js backend
- 🔑 WebAuthn Passkey Support
- 🚀 No Direct Database Handling: The developer provides session storage logic
npm install @mikelambson/secure-auth
Since Secure Auth does not handle databases directly, you need to implement a SessionStore
.
Column Name | Type | Description |
---|---|---|
session_id |
VARCHAR(255) PRIMARY KEY |
Unique session identifier (UUID) |
user_id |
VARCHAR(255) NOT NULL |
References the user who owns the session |
created_at |
TIMESTAMP DEFAULT NOW() |
Timestamp when the session was created |
expires_at |
TIMESTAMP |
Expiry time (optional, if enforcing expiration) |
CREATE TABLE sessions (
session_id VARCHAR(255) PRIMARY KEY,
user_id VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
expires_at TIMESTAMP
);
Example for PostgreSQL (Knex.js):
import { SessionStore } from "@mikelambson/secure-auth";
import knex from "knex";
const db = knex({ client: "pg", connection: process.env.DATABASE_URL });
export class MySessionStore implements SessionStore {
async saveSession(sessionId: string, userId: string): Promise<boolean> {
await db("sessions").insert({ session_id: sessionId, user_id: userId });
return true;
}
async isSessionValid(sessionId: string): Promise<boolean> {
const session = await db("sessions").where({ session_id: sessionId }).first();
return !!session;
}
async deleteSession(sessionId: string): Promise<boolean> {
await db("sessions").where({ session_id: sessionId }).del();
return true;
}
}
Example for Prisma ORM:
import { SessionStore } from "@mikelambson/secure-auth";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export class PrismaSessionStore implements SessionStore {
async saveSession(sessionId: string, userId: string): Promise<boolean> {
await prisma.session.create({ data: { sessionId, userId } });
return true;
}
async isSessionValid(sessionId: string): Promise<boolean> {
const session = await prisma.session.findUnique({ where: { sessionId } });
return !!session;
}
async deleteSession(sessionId: string): Promise<boolean> {
await prisma.session.delete({ where: { sessionId } });
return true;
}
}
import { AuthService } from "@mikelambson/secure-auth";
import { readFileSync } from 'fs';
import { MySessionStore } from "./mySessionStore";
// Load your Ed25519 key pair (e.g., from .env or files)
const privateKey = readFileSync(process.env.JWT_PRIVATE_KEY!, 'utf8');
const publicKey = readFileSync(process.env.JWT_PUBLIC_KEY!, 'utf8');
// Optional: Pass a session store implementation (or null to bypass)
const sessionStore = new MySessionStore(); //from your created store constructor
const authService = new AuthService(privateKey, publicKey, sessionStore);
const newUser = await authService.registerUser({
login: "user@example.com",
password: "securepassword",
});
// Save `newUser` in your database
const tokens = await authService.login(user, "user-password");
console.log(tokens); // { accessToken, refreshToken, sessionId }
const user = await authService.validateSession(accessToken);
console.log(user); // { userId, sessionId }
await authService.logout(sessionId);
const challenge = await authService.registerPasskey(userId, "example.com", "preferred", storeChallenge);
const verified = await authService.verifyPasskey(response, userId, "example.com", "https://example.com", getStoredChallenge, getCredential);
import express from "express";
const app = express();
app.use(express.json());
app.post("/login", async (req, res) => {
try {
const { userId, password } = req.body;
const user = await db.query("SELECT * FROM users WHERE id = $1", [userId]);
if (!user) return res.status(400).json({ error: "User not found" });
const tokens = await authService.login(user, password);
res.json(tokens);
} catch (err) {
res.status(401).json({ error: err.message });
}
});
BSD-3-Clause License. See LICENSE
for details.
- Fork the repository
- Create a feature branch
- Submit a pull request
- GitHub Repository: Secure Auth
- Issues & Bugs: Submit an Issue