540dd9df5b
Auf Wunsch: Betreiber-Bereich getrennt von Kundenkonten. - auth.js: signAdminToken (role:operator), requireAdmin prüft Token-Rolle; requireAuth weist Operator-Token ab (saubere Trennung beide Richtungen) - routes/admin.js: POST /admin/login (ADMIN_PASSWORD → Operator-Token) - env.js: adminPassword statt adminEmail - 0003_admin.sql: droppt die nicht mehr genutzte accounts.is_admin-Spalte - register/login/account/me: is_admin restlos entfernt E2E: Kunde→403, falsches PW→401, richtiges PW→Token, stats→200, Admin-Token→Kundenroute→401. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
53 lines
1.9 KiB
JavaScript
53 lines
1.9 KiB
JavaScript
// HOST-Auth. Zwei getrennte Welten:
|
|
// • Kundenkonten (accounts) — Register/Login, JWT mit {sub,email}
|
|
// • Betreiber/Admin — SEPARATES Login mit ADMIN_PASSWORD, JWT mit {role:operator}
|
|
// Ein Kunde kann NIE Admin werden; Admin ist kein Kundenkonto.
|
|
import bcrypt from "bcryptjs";
|
|
import jwt from "jsonwebtoken";
|
|
import { env } from "./env.js";
|
|
|
|
export async function hashPassword(plain) {
|
|
return bcrypt.hash(plain, 10);
|
|
}
|
|
|
|
export async function verifyPassword(plain, hash) {
|
|
return bcrypt.compare(plain, hash);
|
|
}
|
|
|
|
// — Kunden-Token —
|
|
export function signToken(account) {
|
|
return jwt.sign({ sub: account.id, email: account.email }, env.jwtSecret, { expiresIn: "7d" });
|
|
}
|
|
|
|
// — Admin/Betreiber-Token (eigene Rolle, kürzere Laufzeit) —
|
|
export function signAdminToken() {
|
|
return jwt.sign({ role: "operator" }, env.jwtSecret, { expiresIn: "12h" });
|
|
}
|
|
|
|
// Middleware: eingeloggter Kunde (oder 401).
|
|
export function requireAuth(req, res, next) {
|
|
const token = (req.headers.authorization || "").replace(/^Bearer /, "");
|
|
if (!token) return res.status(401).json({ error: "Nicht angemeldet." });
|
|
try {
|
|
const p = jwt.verify(token, env.jwtSecret);
|
|
if (p.role === "operator") return res.status(401).json({ error: "Admin-Token, kein Kundenkonto." });
|
|
req.account = { id: p.sub, email: p.email };
|
|
next();
|
|
} catch {
|
|
res.status(401).json({ error: "Session ungültig oder abgelaufen." });
|
|
}
|
|
}
|
|
|
|
// Middleware: Betreiber/Admin (Operator-Rolle im Token, oder 403).
|
|
export function requireAdmin(req, res, next) {
|
|
const token = (req.headers.authorization || "").replace(/^Bearer /, "");
|
|
if (!token) return res.status(401).json({ error: "Nicht angemeldet." });
|
|
try {
|
|
const p = jwt.verify(token, env.jwtSecret);
|
|
if (p.role !== "operator") return res.status(403).json({ error: "Kein Admin-Zugriff." });
|
|
next();
|
|
} catch {
|
|
res.status(401).json({ error: "Session ungültig oder abgelaufen." });
|
|
}
|
|
}
|