83 lines
2.7 KiB
JavaScript
83 lines
2.7 KiB
JavaScript
const jwt = require('jsonwebtoken');
|
||
const config = require('../config/env');
|
||
|
||
// Verifiziert das Token und stellt sicher, dass es sich um ein Admin-Token handelt.
|
||
const authenticateToken = (req, res, next) => {
|
||
// Try to get token from cookie first (new secure method)
|
||
let token = req.cookies?.token;
|
||
|
||
// Fallback: Check Authorization header for backward compatibility
|
||
if (!token) {
|
||
const authHeader = req.headers['authorization'];
|
||
token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
|
||
}
|
||
|
||
if (!token) {
|
||
return res.status(401).json({
|
||
success: false,
|
||
message: 'Zugriff verweigert. Kein Token bereitgestellt.'
|
||
});
|
||
}
|
||
|
||
try {
|
||
const decoded = jwt.verify(token, config.jwtSecret);
|
||
|
||
// Reject tokens issued by a different app (C-01 cross-app auth fix)
|
||
if (decoded.app && decoded.app !== config.appName) {
|
||
return res.status(403).json({
|
||
success: false,
|
||
message: 'Ungültiger oder abgelaufener Token.'
|
||
});
|
||
}
|
||
|
||
// Rollenprüfung. Handler-Tokens werden mit demselben Secret signiert; die
|
||
// app-Prüfung oben greift bei ihnen nicht, weil sie keinen app-Claim tragen.
|
||
// Ohne diese Zeilen kann ein eingeloggter Hundeführer seinen Bearer-Token
|
||
// gegen /api/users, /api/config und /api/audit-logs schicken und hat volle
|
||
// Admin-Rechte. Tokens ohne role stammen aus der Zeit davor und wurden
|
||
// ausschließlich für Admins ausgestellt.
|
||
if (decoded.role && decoded.role !== 'admin') {
|
||
return res.status(403).json({
|
||
success: false,
|
||
message: 'Zugriff verweigert. Keine Administratorrechte.'
|
||
});
|
||
}
|
||
|
||
req.user = decoded;
|
||
next();
|
||
} catch (error) {
|
||
return res.status(403).json({
|
||
success: false,
|
||
message: 'Ungültiger oder abgelaufener Token.'
|
||
});
|
||
}
|
||
};
|
||
|
||
// Wie authenticateToken, blockiert aber nicht: setzt req.user wenn ein gültiges
|
||
// Admin-Token vorliegt und ruft ansonsten einfach next(). Für Endpunkte wie
|
||
// /logout, die auch mit abgelaufenem Token funktionieren müssen.
|
||
const attachUserIfPresent = (req, res, next) => {
|
||
let token = req.cookies?.token;
|
||
if (!token) {
|
||
const authHeader = req.headers['authorization'];
|
||
token = authHeader && authHeader.split(' ')[1];
|
||
}
|
||
|
||
if (token) {
|
||
try {
|
||
const decoded = jwt.verify(token, config.jwtSecret);
|
||
const appOk = !decoded.app || decoded.app === config.appName;
|
||
const roleOk = !decoded.role || decoded.role === 'admin';
|
||
if (appOk && roleOk) {
|
||
req.user = decoded;
|
||
}
|
||
} catch (error) {
|
||
// Ungültiges Token ist hier kein Fehler – der Aufrufer wird ohne req.user bedient.
|
||
}
|
||
}
|
||
|
||
next();
|
||
};
|
||
|
||
module.exports = { authenticateToken, attachUserIfPresent };
|