MBO-Tech-IT-Webseite/modules/02-admin-auth/TEMPLATE.md

236 lines
9.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 (
<SessionTimeoutProvider>
{children}
</SessionTimeoutProvider>
);
}
```
### 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)
<Link href="/admin/login">Admin Login</Link>
```
### 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 <SessionTimeoutProvider>:
import { SessionTimeoutProvider } from "@/components/admin/SessionTimeoutProvider";
export default function AdminLayout({ children }) {
return <SessionTimeoutProvider>{children}</SessionTimeoutProvider>;
}
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.
```