Files
TaskMate/backend/middleware/auth.js
2025-12-30 22:49:56 +00:00

194 Zeilen
4.6 KiB
JavaScript

/**
* 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
};