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>
64 lines
2.1 KiB
JavaScript
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();
|
|
}
|