import { validateJwtToken } from '#util/jwtUtils.js'; import { DbUser, UserType } from '#db/schemas/index.js'; import { SafeNSoundError } from '#util/error.js'; import { getUserById } from '#db/dbAccess/index.js'; const shouldLogAuth = process.env['LOG_AUTH'] === 'true'; function logAuth(...params) { if (shouldLogAuth) { console.log('[AUTH]', ...params); } } function getTokenFromHeader(authHeader) { if (!authHeader || !authHeader.startsWith('Bearer ')) { return null; } const parts = authHeader.split(' '); return { scheme: parts[0], token: parts[1] }; } export async function useAuthentication(req, res, next) { if (!req.headers.authorization) { logAuth('Request to', req.path, 'without auth'); next(); return; } const auth = (req.auth = await validateJwtToken( getTokenFromHeader(req.headers.authorization).token )); logAuth('Token data:', auth); if (auth) { req.user = await getUserById(auth.sub); logAuth('User data:', req.user); if (req.user) { req.device = req.user.devices.find( device => device.id === auth.deviceId ); logAuth('Device data:', req.device); if (req.device) { if (req.device.lastSeen < Date.now() - 1000) { logAuth('Updating device last seen for', req.device.id); req.device.lastSeen = Date.now(); await req.user.save(); } } } } next(); } export async function requireAuth(req, res, next) { if (!req.auth || !req.user || !req.device) { logAuth('Unauthorized request to', req.path); res.status(401).send( new SafeNSoundError({ errCode: 'UNAUTHORIZED', message: 'Unauthorized' }) ); return; } next(); } /** * @param options {AuthValidationOptions} * @returns {(function(*, *, *): void)|*} */ export function requireRole(options) { return async function (req, res, next) { // admin can do everything if (req.user.type === UserType.ADMIN) { next(); return; } if (options.roles && !options.roles.includes(req.user.type)) { logAuth('User is missing roles', options.roles); res.status(401).send( new SafeNSoundError({ errCode: 'UNAUTHORIZED', message: 'Unauthorized' }) ); return; } next(); }; } export const requireAdmin = requireRole({ roles: [UserType.ADMIN] }); export const requireMonitor = requireRole({ roles: [UserType.MONITOR] }); export const requireUser = requireRole({ roles: [UserType.USER] }); export const requireUserOrMonitor = requireRole({ roles: [UserType.USER, UserType.MONITOR] }); class AuthValidationOptions { roles; }