import { NextRequest, NextResponse } from "next/server"; import bcrypt from "bcryptjs"; import { createServiceClient } from "@/lib/supabase"; import { createSessionToken, verifySessionToken } from "@/lib/admin-auth"; import { logLoginAttempt, getFailedLoginCount, sendSecurityAlert } from "@/lib/audit-log"; import { checkRateLimit, resetRateLimit } from "@/lib/rate-limit"; import { revokeSessionToken } from "@/lib/token-blacklist"; export async function POST(req: NextRequest) { const { email, password } = await req.json(); const ipAddr = req.headers.get("x-forwarded-for") || req.headers.get("x-real-ip") || "unknown"; const userAgent = req.headers.get("user-agent") || "unknown"; // ──── Validierung ──── if (!email || !password) { await logLoginAttempt("", ipAddr, false, userAgent, "missing_credentials"); return NextResponse.json({ error: "E-Mail und Passwort erforderlich" }, { status: 400 }); } // ──── Rate-Limiting ──── const rateLimitKey = `login:${email.toLowerCase()}:${ipAddr}`; const { allowed, delayMs, locked } = checkRateLimit(rateLimitKey); if (locked) { await logLoginAttempt(email, ipAddr, false, userAgent, "rate_limit_locked"); const res = NextResponse.json( { error: "Zu viele Anmeldeversuche. Bitte später versuchen." }, { status: 429 } ); res.headers.set("Retry-After", String(Math.ceil(delayMs / 1000))); return res; } if (!allowed && delayMs > 0) { // Künstliche Verzögerung um Timing-Attacken zu verhindern await new Promise((resolve) => setTimeout(resolve, delayMs)); } // ──── Datenbankabfrage ──── const db = createServiceClient(); const { data: admin } = await db .from("admin_users") .select("id, email, name, password_hash, aktiv") .eq("email", email.toLowerCase().trim()) .single(); // ──── Admin nicht gefunden oder inaktiv ──── if (!admin || !admin.aktiv) { await logLoginAttempt(email, ipAddr, false, userAgent, "user_not_found_or_inactive"); // Prüfe auf verdächtige Aktivität const failedCount = await getFailedLoginCount(email, "email", 60); if (failedCount >= 10) { await sendSecurityAlert( "⚠️ Viele fehlgeschlagene Login-Versuche", `Email: ${email}\nIPs: Siehe Audit-Logs\nVersuche in der letzten Stunde: ${failedCount}` ); } return NextResponse.json({ error: "Ungültige Zugangsdaten" }, { status: 401 }); } // ──── Passwort validieren ──── const valid = await bcrypt.compare(password, admin.password_hash); if (!valid) { await logLoginAttempt(email, ipAddr, false, userAgent, "invalid_password"); // Prüfe auf Brute-Force-Aktivität const failedCount = await getFailedLoginCount(email, "email", 60); if (failedCount >= 10) { await sendSecurityAlert( "⚠️ Viele fehlgeschlagene Login-Versuche (falsches Passwort)", `Email: ${email}\nIPs: ${ipAddr}\nVersuche in der letzten Stunde: ${failedCount}` ); } return NextResponse.json({ error: "Ungültige Zugangsdaten" }, { status: 401 }); } // ──── Login erfolgreich ──── await logLoginAttempt(email, ipAddr, true, userAgent); resetRateLimit(rateLimitKey); // Rate-Limit zurücksetzen const token = await createSessionToken({ id: admin.id, email: admin.email, name: admin.name }); const res = NextResponse.json({ success: true }); res.cookies.set("admin_session", token, { httpOnly: true, sameSite: "lax", maxAge: 60 * 60 * 2, // ✅ Geändert: 2 Stunden statt 7 Tage path: "/", secure: process.env.NODE_ENV === "production", }); return res; } export async function DELETE(req: NextRequest) { const token = req.cookies.get("admin_session")?.value; if (token) { try { // ✅ Verify token to get admin_id, then add signature to blacklist const session = await verifySessionToken(token); if (session) { const [, sig] = token.split("."); await revokeSessionToken(sig, session.id, "logout"); } } catch (error) { console.error("Error revoking session token:", error); // Continue with logout even if revocation fails } } const res = NextResponse.json({ success: true }); res.cookies.delete("admin_session"); return res; }