import type { Req, Res, Next } from '../types/http'; const crypto = require('crypto'); const { getSessionUser, COOKIE_NAME, SINGLE_COOKIE_NAME, cookieOpts, publicUser, recordLogin, } = require('../services/authService.cts'); const { getDb, getSetting } = require('../db/database.cts'); const { standardizeError } = require('./errorFormatter.cts'); function getSingleModeUser(): any { if (getSetting('auth_mode') !== 'single') return null; const userId = getSetting('default_user_id'); if (!userId) return null; // Single-user mode: validate only that the configured user exists, is active, // and has role 'user'. Sessions are not relevant here. const row = getDb() .prepare( ` SELECT id, username, display_name, role, must_change_password, first_login, active, is_default_admin, last_seen_version FROM users WHERE id = ? AND role = 'user' AND active = 1 `, ) .get(userId); return row ? publicUser(row) : null; } function requireAuth(req: Req, res: Res, next: Next): any { // Single-user mode: bypass session entirely, auto-attach the default user const singleUser = getSingleModeUser(); if (singleUser) { req.user = singleUser; req.singleUserMode = true; // Track logins via a presence cookie so login history works without a real session. const existing = req.cookies?.[SINGLE_COOKIE_NAME]; if (existing) { req.singleSessionId = existing; } else { const sessionId = crypto.randomUUID(); res.cookie(SINGLE_COOKIE_NAME, sessionId, { httpOnly: true, sameSite: 'strict', secure: cookieOpts(req).secure, maxAge: 30 * 86400 * 1000, // 30 days path: '/', }); req.singleSessionId = sessionId; // Non-blocking — don't delay the first request setImmediate(() => { try { recordLogin(singleUser.id, req.ip, req.get('user-agent'), sessionId); } catch {} }); } return next(); } const user = getSessionUser(req.cookies?.[COOKIE_NAME]); // AUTH_REQUIRED (not AUTH_ERROR): the one code that means "no valid session". // The client keys its session-expired redirect off this code — wrong-credential // 401s elsewhere (change-password, TOTP disable, …) use AUTH_ERROR and must // NOT log the user out. if (!user) return res.status(401).json(standardizeError('Not authenticated', 'AUTH_REQUIRED')); req.user = user; next(); } function requireUser(req: Req, res: Res, next: Next): any { if (req.user?.is_default_admin) { return res .status(403) .json(standardizeError('Default admin account does not have tracker access', 'FORBIDDEN')); } if (!['user', 'admin'].includes(req.user?.role)) { return res .status(403) .json(standardizeError('Access denied: user account required', 'FORBIDDEN')); } next(); } function requireAdmin(req: Req, res: Res, next: Next): any { // In single-user mode the auto-attached user is never admin, // so admin routes naturally stay protected by session. if (req.user?.role !== 'admin') { return res .status(403) .json(standardizeError('Access denied: admin account required', 'FORBIDDEN')); } next(); } module.exports = { requireAuth, requireUser, requireAdmin };