/** * TASKMATE - Auth Middleware * ========================== * JWT-basierte Authentifizierung */ const jwt = require('jsonwebtoken'); const logger = require('../utils/logger'); const JWT_SECRET = process.env.JWT_SECRET || 'UNSICHER_BITTE_AENDERN'; const SESSION_TIMEOUT = parseInt(process.env.SESSION_TIMEOUT) || 30; // Minuten /** * JWT-Token generieren */ function generateToken(user) { // Permissions parsen falls als String gespeichert let permissions = user.permissions || []; if (typeof permissions === 'string') { try { permissions = JSON.parse(permissions); } catch (e) { permissions = []; } } return jwt.sign( { id: user.id, username: user.username, displayName: user.display_name, color: user.color, role: user.role || 'user', permissions: permissions }, JWT_SECRET, { expiresIn: `${SESSION_TIMEOUT}m` } ); } /** * JWT-Token verifizieren */ function verifyToken(token) { try { return jwt.verify(token, JWT_SECRET); } catch (error) { // Nur bei unerwarteten Fehlern loggen (nicht bei normalen Ablauf/Ungültig-Fällen) if (error.name !== 'TokenExpiredError' && error.name !== 'JsonWebTokenError') { logger.error(`[AUTH] Unerwarteter Token-Fehler: ${error.name} - ${error.message}`); } return null; } } /** * Express Middleware: Token aus Header, Cookie oder Query-Parameter prüfen */ function authenticateToken(req, res, next) { // Token aus Authorization Header, Cookie oder Query-Parameter (für img src etc.) let token = null; const authHeader = req.headers['authorization']; if (authHeader && authHeader.startsWith('Bearer ')) { token = authHeader.substring(7); } else if (req.cookies && req.cookies.token) { token = req.cookies.token; } else if (req.query && req.query.token) { // Token aus Query-Parameter (für Ressourcen die in img/video tags geladen werden) token = req.query.token; } if (!token) { return res.status(401).json({ error: 'Nicht authentifiziert' }); } const user = verifyToken(token); if (!user) { return res.status(401).json({ error: 'Token ungültig oder abgelaufen' }); } // User-Info an Request anhängen req.user = user; // Token-Refresh: Wenn Token bald ablaeuft, neuen ausstellen const tokenExp = user.exp * 1000; // exp ist in Sekunden const now = Date.now(); const refreshThreshold = 5 * 60 * 1000; // 5 Minuten vor Ablauf if (tokenExp - now < refreshThreshold) { const newToken = generateToken({ id: user.id, username: user.username, display_name: user.displayName, color: user.color, role: user.role, permissions: user.permissions }); res.setHeader('X-New-Token', newToken); } next(); } /** * Middleware: Nur Admins erlauben */ function requireAdmin(req, res, next) { if (!req.user) { return res.status(401).json({ error: 'Nicht authentifiziert' }); } if (req.user.role !== 'admin') { return res.status(403).json({ error: 'Keine Admin-Berechtigung' }); } next(); } /** * Middleware: Nur regulaere User erlauben (blockiert Admins) */ function requireRegularUser(req, res, next) { if (!req.user) { return res.status(401).json({ error: 'Nicht authentifiziert' }); } if (req.user.role === 'admin') { return res.status(403).json({ error: 'Admin hat keinen Zugang zur regulaeren App' }); } next(); } /** * Middleware-Factory: Bestimmte Berechtigung pruefen */ function checkPermission(permission) { return (req, res, next) => { if (!req.user) { return res.status(401).json({ error: 'Nicht authentifiziert' }); } const permissions = req.user.permissions || []; if (!permissions.includes(permission)) { return res.status(403).json({ error: `Berechtigung "${permission}" fehlt` }); } next(); }; } /** * Socket.io Middleware: Token prüfen */ function authenticateSocket(socket, next) { const token = socket.handshake.auth.token || socket.handshake.headers.authorization?.replace('Bearer ', ''); if (!token) { return next(new Error('Nicht authentifiziert')); } const user = verifyToken(token); if (!user) { return next(new Error('Token ungültig oder abgelaufen')); } // User-Info an Socket anhängen socket.user = user; next(); } /** * CSRF-Token generieren (für Forms) */ function generateCsrfToken() { const { randomBytes } = require('crypto'); return randomBytes(32).toString('hex'); } module.exports = { generateToken, verifyToken, authenticateToken, authenticateSocket, generateCsrfToken, requireAdmin, requireRegularUser, checkPermission, JWT_SECRET, SESSION_TIMEOUT };