Dual Authentication Guide
Goal: Secure your Webspresso application with stateful Session Cookies for SSR pages and stateless HS256 JWT tokens with refresh rotation for REST APIs.
1. Dual Auth Overview #
Webspresso provides a zero-dependency dual authentication system (core/auth/):
- Stateful Session Auth: Designed for browser users navigating server-rendered
.njkpages with secureHttpOnlycookies. - Stateless JWT Auth: Designed for mobile apps, external clients, and SPA frontend frameworks calling
pages/api/*endpoints.
2. Setting Up Session Authentication #
Configure createAuth in server.js:
const { createApp } = require('webspresso');
const { createAuth } = require('webspresso/core/auth');
const auth = createAuth({
secret: process.env.SESSION_SECRET || 'super-secret-key-at-least-32-chars',
session: {
cookie: {
secure: process.env.NODE_ENV === 'production',
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
},
},
});
const { app } = createApp({
auth,
pagesDir: path.join(__dirname, 'pages'),
viewsDir: path.join(__dirname, 'views'),
});
Logging In & Out in Route Handlers #
// pages/api/auth/login.post.js
module.exports = {
async handler(req, res) {
const { email, password } = req.body;
// Verify credentials with your service
const user = await req.service('user.verify-password', { email, password });
// Establish session
req.session.user = { id: user.id, email: user.email, role: user.role };
res.json({ success: true, user: req.session.user });
},
};
3. Setting Up Stateless JWT Authentication #
Generate and verify HMAC-SHA256 tokens using the native crypto JWT core:
const { signJwt, verifyJwt } = require('webspresso/core/auth/jwt');
// Sign token (15-minute access token)
const token = signJwt(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
Protecting API Routes with JWT Middleware #
Use the built-in jwt middleware in your API route definition:
// pages/api/account/profile.get.js
module.exports = {
middleware: ['jwt'], // Requires valid Authorization: Bearer <token>
async handler(req, res) {
// req.auth contains the decoded JWT payload
res.json({
userId: req.auth.userId,
role: req.auth.role,
});
},
};
4. Policy Guards & RBAC #
Protect sensitive endpoints with declarative role requirements:
// In page loaders or API routes
const { requireRole } = require('webspresso/core/auth');
module.exports = {
middleware: [requireRole(['admin', 'editor'])],
async load({ req }) {
return { adminData: 'confidential' };
},
};
5. Active Devices & Remote Session Revocation #
Webspresso provides built-in zero-dependency device tracking and remote session revocation (webspresso/core/auth):
const { quickAuth, createUserSessionsTable } = require('webspresso/core/auth');
// Migration: create user_sessions table
await createUserSessionsTable(db.knex);
// Enable session tracking
const auth = quickAuth({
db,
sessionTracking: true,
});
Listing Active Sessions & Devices #
// pages/api/auth/sessions.get.js
const { defineApi } = require('webspresso');
module.exports = defineApi({
middleware: ['auth'],
async handler(req, res) {
const sessions = await req.auth.sessions();
// Returns: [{ session_id, ip_address, browser: 'Chrome 128', os: 'macOS 14', device: 'desktop', last_active_at, is_current: true }]
return res.json({ ok: true, sessions });
},
});
Revoking a Remote Session #
// pages/api/auth/sessions/revoke.post.js
const { defineApi } = require('webspresso');
module.exports = defineApi({
middleware: ['auth'],
schema: ({ z }) => ({
body: z.object({
sessionId: z.string().optional(),
allOthers: z.boolean().optional(),
}),
}),
async handler(req, res) {
const { sessionId, allOthers } = req.input.body;
if (allOthers) {
const count = await req.auth.revokeOtherSessions();
return res.json({ ok: true, revokedCount: count });
}
await req.auth.revokeSession(sessionId);
return res.json({ ok: true });
},
});