const jwt = require('jsonwebtoken') require('dotenv').config() const log = require('./logger')('auth') const JWT_SECRET = process.env.JWT_SECRET const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '1d' const COOKIE_NAME = process.env.COOKIE_NAME || 'uomm_token' if (!JWT_SECRET) { log.warn('JWT_SECRET is not set — set it in .env before going to production') } function signToken(user) { const payload = { id: user.id, username: user.username, role: user.role } return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN }) } function verifyToken(token) { try { return jwt.verify(token, JWT_SECRET) } catch (err) { return null } } // Rough max-age (ms) for the cookie, parsed from JWT_EXPIRES_IN (e.g. 1d, 12h, 30m). function cookieMaxAge() { const m = /^(\d+)([dhms])$/.exec(String(JWT_EXPIRES_IN).trim()) if (!m) return 24 * 60 * 60 * 1000 const n = Number(m[1]) const unit = { d: 86400000, h: 3600000, m: 60000, s: 1000 }[m[2]] return n * unit } /** * Decide the cookie Secure flag. COOKIE_SECURE=auto (default) uses req.secure, * which is true behind Pangolin (HTTPS, X-Forwarded-Proto) and false over plain * HTTP on the LAN IP — so login works in both. Requires app.set('trust proxy'). */ function cookieSecure(req) { const mode = (process.env.COOKIE_SECURE || 'auto').toLowerCase() if (mode === 'true') return true if (mode === 'false') return false return Boolean(req.secure) } function cookieOptions(req) { return { httpOnly: true, sameSite: 'lax', secure: cookieSecure(req), path: '/', } } function setAuthCookie(req, res, token) { res.cookie(COOKIE_NAME, token, { ...cookieOptions(req), maxAge: cookieMaxAge() }) } function clearAuthCookie(req, res) { res.clearCookie(COOKIE_NAME, cookieOptions(req)) } // Extract a token from the cookie or an Authorization: Bearer header. function extractToken(req) { if (req.cookies && req.cookies[COOKIE_NAME]) return req.cookies[COOKIE_NAME] const header = req.headers.authorization if (header && header.startsWith('Bearer ')) return header.substring(7) return null } // Returns the decoded user or null without rejecting the request. function getUserFromRequest(req) { const token = extractToken(req) if (!token) return null return verifyToken(token) } // Gate middleware for protected (admin) routes. function isLoggedIn(req, res, next) { const user = getUserFromRequest(req) if (!user) return res.status(401).json({ message: 'Unauthorized' }) req.user = user return next() } module.exports = { COOKIE_NAME, signToken, verifyToken, setAuthCookie, clearAuthCookie, getUserFromRequest, isLoggedIn, }