๐ Demo server
โ Open the Live Interactive Demo โ register, login, and explore the API right in your browser. No installation needed.
A complete, self-contained Express server you can run locally in under two minutes.
No database. No cloud. No environment variables to configure โ just copy, install, and go.
This demo uses in-memory storage. All registered users vanish when the server stops โ perfect for exploring the library without any setup.
1 ยท Install dependenciesโ
npm install @nik2208/node-auth express cookie-parser
npm install -D typescript ts-node @types/node @types/express @types/cookie-parser
2 ยท Complete demo serverโ
Save the file below as server.ts and run it โ everything is self-contained.
import express from 'express';
import cookieParser from 'cookie-parser';
import {
AuthConfigurator,
createAdminRouter,
PasswordService,
IUserStore,
BaseUser,
ISettingsStore,
AuthSettings,
} from '@nik2208/node-auth';
// โโ 1. In-memory user store โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// Stores users in a plain Map<string, BaseUser>.
// All data is lost when the server restarts โ do not use in production.
class InMemoryUserStore implements IUserStore {
private users = new Map<string, BaseUser>();
private nextId = 1;
async findByEmail(email: string) {
return [...this.users.values()].find(u => u.email === email) ?? null;
}
async findById(id: string) { return this.users.get(id) ?? null; }
async create(data: Partial<BaseUser>): Promise<BaseUser> {
const id = String(this.nextId++);
const user: BaseUser = { id, email: data.email ?? '', ...data };
this.users.set(id, user);
return user;
}
async updateRefreshToken(id: string, token: string | null, expiry: Date | null) {
const u = this.users.get(id);
if (u) { u.refreshToken = token; u.refreshTokenExpiry = expiry; }
}
async updateLastLogin(id: string) {
const u = this.users.get(id); if (u) u.lastLogin = new Date();
}
async updateResetToken(id: string, token: string | null, expiry: Date | null) {
const u = this.users.get(id);
if (u) { u.resetToken = token; u.resetTokenExpiry = expiry; }
}
async updatePassword(id: string, hashed: string) {
const u = this.users.get(id); if (u) u.password = hashed;
}
async updateTotpSecret(id: string, secret: string | null) {
const u = this.users.get(id);
if (u) { u.totpSecret = secret; u.isTotpEnabled = secret !== null; }
}
async updateMagicLinkToken(id: string, token: string | null, expiry: Date | null) {
const u = this.users.get(id);
if (u) { u.magicLinkToken = token; u.magicLinkTokenExpiry = expiry; }
}
async updateSmsCode(id: string, code: string | null, expiry: Date | null) {
const u = this.users.get(id);
if (u) { u.smsCode = code; u.smsCodeExpiry = expiry; }
}
// Required for the admin panel users table and user deletion:
async listUsers(limit: number, offset: number) {
return [...this.users.values()].slice(offset, offset + limit);
}
async deleteUser(id: string) { this.users.delete(id); }
}
// โโ 2. In-memory settings store โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// Enables the โ๏ธ Control tab in the admin panel (email verification policy,
// mandatory 2FA toggle, lazy grace-period days, etc.).
class InMemorySettingsStore implements ISettingsStore {
private settings: AuthSettings = {};
async getSettings() { return { ...this.settings }; }
async updateSettings(updates: Partial<AuthSettings>) {
this.settings = { ...this.settings, ...updates };
}
}
// โโ 3. Wire everything together โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const app = express();
app.use(express.json());
app.use(cookieParser()); // needed to read/write JWT cookies
const userStore = new InMemoryUserStore();
const settingsStore = new InMemorySettingsStore();
const passwordService = new PasswordService();
const config = {
accessTokenSecret: 'demo-access-secret-change-in-production',
refreshTokenSecret: 'demo-refresh-secret-change-in-production',
accessTokenExpiresIn: '15m',
refreshTokenExpiresIn: '7d',
cookieOptions: { secure: false, sameSite: 'lax' as const },
};
const auth = new AuthConfigurator(config, userStore);
// โโ 4. Auth routes โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// POST /auth/register โ create a new account
// POST /auth/login โ email + password โ sets HttpOnly JWT cookies
// GET /auth/me โ return the authenticated user's profile
// POST /auth/logout โ clear JWT cookies
// POST /auth/refresh โ rotate the access token using the refresh cookie
app.use('/auth', auth.router({
onRegister: async (data) => {
const hashed = await passwordService.hash(data.password as string);
return userStore.create({
email: data.email as string,
password: hashed,
role: 'user',
});
},
}));
// โโ 5. A custom protected route โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// auth.middleware() verifies the access-token cookie (or Bearer header).
// req.user contains { sub, email, role, ... } from the JWT payload.
app.get('/profile', auth.middleware(), (req, res) => {
res.json({ message: 'You are authenticated!', user: req.user });
});
// โโ 6. Admin panel โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// Browse to http://localhost:3000/admin and enter the password below.
// The built-in UI lets you list/delete users and tweak auth settings.
app.use('/admin', createAdminRouter(userStore, {
adminSecret: '1234', // โ change this in production!
settingsStore,
}));
// โโ 7. Start โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
app.listen(3000, () => {
console.log('');
console.log(' Demo running โ http://localhost:3000');
console.log(' Admin panel โ http://localhost:3000/admin (password: 1234)');
console.log('');
});
3 ยท Run itโ
npx ts-node server.ts
4 ยท Available endpointsโ
| Method | Path | Auth required | Description |
|---|---|---|---|
POST | /auth/register | โ | Register a new user (email + password in body) |
POST | /auth/login | โ | Login โ sets accessToken + refreshToken HttpOnly cookies |
GET | /auth/me | โ | Return the authenticated user's profile |
POST | /auth/logout | โ | Clear the JWT cookies |
POST | /auth/refresh | โ | Rotate the access token using the refresh cookie |
GET | /profile | โ | Custom protected route example |
GET | /admin | โ | Admin panel UI (password: 1234) |
5 ยท Try it with curlโ
Register an account:
curl -c cookies.txt -s -X POST http://localhost:3000/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"alice@example.com","password":"secret123"}' | jq
Login:
curl -c cookies.txt -s -X POST http://localhost:3000/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"alice@example.com","password":"secret123"}' | jq
Fetch your profile (reads the saved cookies):
curl -b cookies.txt -s http://localhost:3000/auth/me | jq
Hit the custom protected route:
curl -b cookies.txt -s http://localhost:3000/profile | jq
Refresh tokens:
curl -b cookies.txt -c cookies.txt -s -X POST http://localhost:3000/auth/refresh | jq
Logout:
curl -b cookies.txt -c cookies.txt -s -X POST http://localhost:3000/auth/logout
6 ยท Admin panelโ
Open http://localhost:3000/admin in your browser.
When the login dialog appears enter:
Password:
1234
The built-in admin dashboard gives you:
- ๐ค Users tab โ list all registered users, view details, delete accounts
- โ๏ธ Control tab โ toggle email verification policy (
none/lazy/strict), enforce mandatory 2FA globally
Before going live replace every placeholder with real values:
# Use strong random secrets (e.g. openssl rand -hex 32)
ACCESS_TOKEN_SECRET=...
REFRESH_TOKEN_SECRET=...
ADMIN_SECRET=...
Also set cookieOptions.secure: true and sameSite: 'strict' (or 'none' for
cross-origin with HTTPS).