190 Zeilen
4.4 KiB
JavaScript
190 Zeilen
4.4 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) {
|
|
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
|
|
};
|