236 lines
9.4 KiB
Markdown
236 lines
9.4 KiB
Markdown
# 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.
|
||
```
|