# Modul: Admin-Auth & Security > Vollständige Admin-Authentifizierung: HMAC-SHA256 Session-Tokens (2h), Brute-Force-Schutz (Rate-Limit + exponentieller Backoff), Token-Blacklist (Logout-Revocation), One-Time-Use Email-Action-Links, Session-Timeout-Provider (15-Min-Warnung), Audit-Logging aller Login-Versuche. --- ## Enthaltene Dateien | Ziel im neuen Projekt | Inhalt | |---|---| | `lib/admin-auth.ts` | Token-Erzeugung/Verifikation (HMAC-SHA256), `requireAdmin()` Middleware | | `lib/rate-limit.ts` | In-Memory Rate-Limiting mit Backoff (5 Versuche/15 Min, Lock nach 10) | | `lib/token-blacklist.ts` | Session-Token Revocation + Action-Token One-Time-Use (Supabase) | | `lib/audit-log.ts` | Login-Audit-Logging + Brute-Force-Erkennung | | `app/api/admin/login/route.ts` | POST: Login, DELETE: Logout | | `app/api/admin/anfragen-action/route.ts` | GET: Email-Action-Link verarbeiten (HMAC-Token) | | `app/admin/login/page.tsx` | Login-Seite (Open-Redirect-Schutz) | | `app/admin/audit-logs/page.tsx` | Admin-Dashboard: Login-Überwachung | | `components/admin/SessionTimeoutProvider.tsx` | Client-seitiger Inaktivitäts-Tracker (Warnung + Auto-Logout) | | `migrations/MIGRATIONS_TOKEN_BLACKLIST.sql` | Tabellen: `admin_session_blacklist`, `action_token_blacklist` | | `migrations/MIGRATIONS_AUDIT_LOGS.sql` | Tabelle: `admin_audit_logs` | --- ## Voraussetzungen ```bash npm install bcryptjs npm install -D @types/bcryptjs ``` Benötigt außerdem: - `lib/supabase.ts` mit Service Client - Supabase-Tabelle `benutzer` (Admin-User-Tabelle, siehe unten) --- ## Umgebungsvariablen (`.env.local`) ```env NEXTAUTH_SECRET=min-32-zeichen-zufaelliger-string # HMAC-Schlüssel für Tokens ``` --- ## Datenbank-Migrationen (Supabase) ### 1. Migrations aus `migrations/` ausführen ``` MIGRATIONS_AUDIT_LOGS.sql → Tabelle admin_audit_logs MIGRATIONS_TOKEN_BLACKLIST.sql → Tabellen admin_session_blacklist, action_token_blacklist ``` ### 2. Admin-User-Tabelle (muss im Projekt existieren) ```sql CREATE TABLE benutzer ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), email text UNIQUE NOT NULL, password_hash text NOT NULL, -- bcrypt-Hash name text, active boolean DEFAULT true, created_at timestamptz DEFAULT now() ); ``` ### 3. Supabase-Typen ergänzen (`lib/supabase.ts`) In `Database.public.Tables` ergänzen: ```ts benutzer: { Row: { id: string; email: string; password_hash: string; active: boolean } } admin_audit_logs: { Row: { id: string; email: string; ip_addr: string; user_agent: string; success: boolean; reason: string | null; timestamp: string } } admin_session_blacklist: { Row: { id: string; admin_id: string; token_signature: string; revoked_at: string; reason: string } } action_token_blacklist: { Row: { id: string; anfrage_id: string; token_signature: string; action_type: string; used_at: string } } ``` --- ## Einbindung Schritt für Schritt ### 1. Dateien kopieren Alle `files/` in entsprechende Projektpfade. ### 2. Admin-Layout absichern (`app/admin/layout.tsx`) ```tsx import { SessionTimeoutProvider } from "@/components/admin/SessionTimeoutProvider"; export default function AdminLayout({ children }) { return ( {children} ); } ``` ### 3. Admin-API-Routes schützen ```ts import { requireAdmin } from "@/lib/admin-auth"; export async function GET(req: Request) { const admin = await requireAdmin(req); if (!admin.ok) return admin.response; // ... Route-Logik } ``` ### 4. Login-Route in Nav verlinken ```tsx // Redirect nach Login zu /admin (oder ?from=/admin/anfragen) Admin Login ``` ### 5. Ersten Admin-User anlegen ```sql -- bcrypt-Hash generieren (cost factor 12) und direkt eintragen INSERT INTO benutzer (email, password_hash) VALUES ('admin@example.com', '$2b$12$...'); ``` Oder via Script: ```ts import bcrypt from "bcryptjs"; const hash = await bcrypt.hash("password", 12); ``` ### 6. Action-Links (für Email-Buttons) Wenn Action-Links aus Emails verarbeitet werden sollen (z.B. Anfrage bestätigen per Klick): - `app/api/admin/anfragen-action/route.ts` verarbeitet `GET ?token=...` - Token erzeugen mit `createActionToken(id, "bestaetigt")` aus `lib/admin-auth.ts` - Route anpassen: Was passiert nach erfolgreicher Aktion (DB-Update + Redirect) --- ## Anpassungspunkte | Was | Wo | |---|---| | Session-Dauer (aktuell 2h) | `lib/admin-auth.ts` → `SESSION_DURATION` | | Action-Token Gültigkeit (7 Tage) | `lib/admin-auth.ts` → `ACTION_TOKEN_DURATION` | | Rate-Limit Schwellenwert (5 Versuche) | `lib/rate-limit.ts` → `MAX_ATTEMPTS` | | Account-Lock Dauer (15 Min) | `lib/rate-limit.ts` → `LOCK_DURATION_MS` | | Inaktivitäts-Timeout (2h) | `components/admin/SessionTimeoutProvider.tsx` → `TIMEOUT_MS` | | Warn-Zeitpunkt (15 Min vor Ablauf) | `components/admin/SessionTimeoutProvider.tsx` → `WARN_BEFORE_MS` | | Audit-Log Aufbewahrung (90 Tage) | `lib/audit-log.ts` → `deleteOldAuditLogs()` | --- ## Integrations-Prompt Kopiere diesen Prompt in eine neue KI-Konversation, nachdem du die `files/` in dein Projekt kopiert hast. Ersetze alle `[PLATZHALTER]`. ``` Ich integriere das Admin-Auth-Modul (HMAC-Session, Rate-Limit, Token-Blacklist, Audit-Log) in mein Next.js/Supabase-Projekt. PROJEKT-KONTEXT: - Erster Admin-User: Email [ADMIN_EMAIL], Passwort [ADMIN_PASSWORT] - Admin-Bereich URL-Prefix: /admin - Supabase: bereits eingerichtet, lib/supabase.ts vorhanden BEREITS KOPIERTE DATEIEN (aus modules/02-admin-auth/files/): - lib/admin-auth.ts, lib/rate-limit.ts, lib/token-blacklist.ts, lib/audit-log.ts - app/api/admin/login/route.ts - app/api/admin/anfragen-action/route.ts - app/admin/login/page.tsx - app/admin/audit-logs/page.tsx - components/admin/SessionTimeoutProvider.tsx AUFGABEN – führe sie der Reihe nach aus: 1. SUPABASE-MIGRATIONEN: Führe diese SQLs nacheinander im Supabase SQL-Editor aus: -- Aus migrations/MIGRATIONS_AUDIT_LOGS.sql: CREATE TABLE IF NOT EXISTS admin_audit_logs ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), email text NOT NULL, ip_addr text NOT NULL, user_agent text NOT NULL, success boolean NOT NULL, reason text, timestamp timestamptz DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_audit_logs_email ON admin_audit_logs(email); CREATE INDEX IF NOT EXISTS idx_audit_logs_timestamp ON admin_audit_logs(timestamp DESC); ALTER TABLE admin_audit_logs ENABLE ROW LEVEL SECURITY; CREATE POLICY "Admin Logs lesen" ON admin_audit_logs FOR SELECT USING (true); -- Aus migrations/MIGRATIONS_TOKEN_BLACKLIST.sql: CREATE TABLE admin_session_blacklist ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), admin_id uuid NOT NULL, token_signature text NOT NULL UNIQUE, revoked_at timestamptz DEFAULT now(), reason text NOT NULL, notes text ); CREATE INDEX idx_admin_session_blacklist_sig ON admin_session_blacklist(token_signature); CREATE TABLE action_token_blacklist ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), anfrage_id uuid NOT NULL, token_signature text NOT NULL UNIQUE, action_type text NOT NULL, used_at timestamptz DEFAULT now(), used_by_ip text, notes text ); CREATE INDEX idx_action_token_blacklist_sig ON action_token_blacklist(token_signature); ALTER TABLE admin_session_blacklist ENABLE ROW LEVEL SECURITY; ALTER TABLE action_token_blacklist ENABLE ROW LEVEL SECURITY; 2. ADMIN-USER-TABELLE: Prüfe ob eine Tabelle für Admin-Benutzer existiert. Falls nicht, erstelle: CREATE TABLE benutzer ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), email text UNIQUE NOT NULL, password_hash text NOT NULL, name text, active boolean DEFAULT true, created_at timestamptz DEFAULT now() ); 3. ERSTEN ADMIN-USER ANLEGEN: Generiere einen bcrypt-Hash für [ADMIN_PASSWORT] (cost 12) und füge diesen direkt per SQL ein: INSERT INTO benutzer (email, password_hash, name) VALUES ('[ADMIN_EMAIL]', '[HASH]', 'Admin'); 4. SUPABASE-TYPEN: Lies lib/supabase.ts und ergänze in Database.public.Tables: benutzer: { Row: { id: string; email: string; password_hash: string; active: boolean; name: string | null } } admin_audit_logs: { Row: { id: string; email: string; ip_addr: string; user_agent: string; success: boolean; reason: string | null; timestamp: string } } admin_session_blacklist: { Row: { id: string; admin_id: string; token_signature: string; revoked_at: string; reason: string } } action_token_blacklist: { Row: { id: string; anfrage_id: string; token_signature: string; action_type: string; used_at: string } } 5. ENV-VARIABLEN: Ergänze .env.local um: NEXTAUTH_SECRET=[MIN_32_ZEICHEN_ZUFAELLIGER_STRING] 6. ADMIN-LAYOUT absichern: Lies app/admin/layout.tsx (oder erstelle es). Importiere und wrappe mit : import { SessionTimeoutProvider } from "@/components/admin/SessionTimeoutProvider"; export default function AdminLayout({ children }) { return {children}; } 7. API-ROUTES schützen: Zeige mir alle Dateien unter app/api/admin/ (außer login/). Füge in jede Route am Anfang der Handler-Funktion ein: import { requireAdmin } from "@/lib/admin-auth"; const admin = await requireAdmin(req); if (!admin.ok) return admin.response; 8. TEST: Starte Dev-Server, öffne /admin/login, logge dich mit [ADMIN_EMAIL] ein. Prüfe: Weiterleitung zu /admin nach Login, /admin/audit-logs zeigt Login-Eintrag. Lies jede Datei vor dem Bearbeiten. Melde wenn alle Schritte abgeschlossen sind. ```