Files
perinco-CRM/server/src/middleware/auth.js
T
Sascha WolfandClaude Opus 5.5 e2ad8ab03e feat(auth): Sitzungen laufen standardmaessig 7 Tage
JWT_EXPIRES_IN faellt ohne Eintrag in .env jetzt auf 7d statt 12h zurueck.
Doku und .env.example angepasst.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 22:05:24 +02:00

64 lines
2.1 KiB
JavaScript

import jwt from 'jsonwebtoken';
import { pool } from '../db.js';
function secret() {
const value = process.env.JWT_SECRET;
if (!value || value.length < 16) {
throw new Error('JWT_SECRET fehlt oder ist zu kurz (min. 16 Zeichen).');
}
return value;
}
export function signToken(user) {
return jwt.sign({ sub: user.id, role: user.role }, secret(), {
expiresIn: process.env.JWT_EXPIRES_IN || '7d',
});
}
/**
* Prueft das Bearer-Token und laedt den aktuellen Benutzer aus der DB.
*
* Die Spaltenliste ist bewusst vollstaendig und kein `SELECT *`: das
* Passwort-Hash soll nicht am Request haengen. Sie muss aber alles enthalten,
* was publicUser() ausliefert - theme und theme_mode gehoeren dazu, sonst
* antwortet GET /api/auth/me mit null und das Frontend haelt die gespeicherte
* Wahl beim Neuladen fuer "nichts gewaehlt".
*/
export async function requireAuth(req, res, next) {
const header = req.headers.authorization || '';
const token = header.startsWith('Bearer ') ? header.slice(7) : null;
if (!token) return res.status(401).json({ error: 'Nicht angemeldet.' });
let payload;
try {
payload = jwt.verify(token, secret());
} catch {
return res.status(401).json({ error: 'Sitzung abgelaufen. Bitte neu anmelden.' });
}
try {
const [rows] = await pool.query(
`SELECT id, username, email, name, role, color, avatar, theme, theme_mode,
is_active, created_at, updated_at
FROM crm_users WHERE id = ? LIMIT 1`,
[payload.sub]
);
const user = rows[0];
if (!user) return res.status(401).json({ error: 'Benutzer existiert nicht mehr.' });
if (!user.is_active) return res.status(403).json({ error: 'Benutzerkonto ist deaktiviert.' });
req.user = { ...user, is_active: Boolean(user.is_active) };
next();
} catch (err) {
next(err);
}
}
/** Nur Rolle admin - schuetzt Einstellungen und Nutzerverwaltung. */
export function requireAdmin(req, res, next) {
if (req.user?.role !== 'admin') {
return res.status(403).json({ error: 'Diese Aktion ist Administratoren vorbehalten.' });
}
next();
}