- replace bearer auth with session+CSRF flow and add admin user directory - update frontend moderation flow, force password change gate, and new CLI - refresh changelog/docs/feature plan + ensure swagger dev experience
376 lines
11 KiB
Markdown
376 lines
11 KiB
Markdown
# Frontend Migration Guide - API Umstrukturierung
|
||
|
||
**Datum:** 16. November 2025
|
||
**Betrifft:** ALLE API-Aufrufe im Frontend
|
||
**Status:** ⚠️ Aktion erforderlich - ALLE Routen prüfen!
|
||
|
||
---
|
||
|
||
## <20> BREAKING CHANGE: Konsistente `/api` Prefixes
|
||
|
||
**ALLE API-Routen haben sich geändert!**
|
||
|
||
### Vorher (inkonsistent):
|
||
```javascript
|
||
// Teils mit /api
|
||
fetch('/api/upload/batch')
|
||
fetch('/api/manage/xyz')
|
||
|
||
// Teils OHNE /api - FALSCH!
|
||
fetch('/groups/123')
|
||
fetch('/groups/123/approve')
|
||
fetch('/moderation/groups/123')
|
||
```
|
||
|
||
### Jetzt (konsistent):
|
||
```javascript
|
||
// ALLE Routen mit /api Prefix
|
||
fetch('/api/upload/batch')
|
||
fetch('/api/manage/xyz')
|
||
fetch('/api/groups/123') // Public
|
||
fetch('/api/admin/groups/123/approve') // Admin
|
||
fetch('/api/admin/groups/123') // Admin
|
||
```
|
||
|
||
---
|
||
|
||
## 🔒 Admin API Authentication
|
||
|
||
Alle Admin-Endpoints (`/api/admin/*` und `/api/system/*`) benötigen jetzt **Bearer Token Authentication**.
|
||
|
||
### Route-Hierarchie
|
||
|
||
1. **Public API**: `/api/*`
|
||
- Öffentlich zugänglich
|
||
- `/api/upload`, `/api/groups`, `/api/download`, etc.
|
||
|
||
2. **Management API**: `/api/manage/*`
|
||
- Token-basiert (UUID aus Upload-Response)
|
||
- Für Gruppenbesitzer
|
||
|
||
3. **Admin API**: `/api/admin/*` ⚠️ **BEARER TOKEN ERFORDERLICH**
|
||
- Moderation, Logs, Consents
|
||
- `/api/admin/groups`, `/api/admin/deletion-log`, etc.
|
||
|
||
4. **System API**: `/api/system/migration/*` ⚠️ **BEARER TOKEN ERFORDERLICH**
|
||
- Wartungsfunktionen
|
||
|
||
### Betroffene Admin-Endpoints
|
||
|
||
- `/api/admin/groups` - Gruppen auflisten
|
||
- `/api/admin/groups/:id` - Gruppe abrufen
|
||
- `/api/admin/groups/:id/approve` - Gruppe genehmigen
|
||
- `/api/admin/groups/:id` - Gruppe löschen
|
||
- `/api/admin/groups/:id/images/:imageId` - Bild löschen
|
||
- `/api/admin/groups/by-consent` - Nach Consent filtern
|
||
- `/api/admin/consents/export` - Consent-Export
|
||
- `/api/admin/social-media/platforms` - Plattformen auflisten
|
||
- `/api/admin/reorder/:groupId/images` - Bilder neu anordnen
|
||
- `/api/admin/deletion-log` - Deletion Log
|
||
- `/api/admin/cleanup/*` - Cleanup-Funktionen
|
||
- `/api/admin/rate-limiter/stats` - Rate-Limiter-Statistiken
|
||
- `/api/admin/management-audit` - Audit-Log
|
||
|
||
**System-Endpoints:**
|
||
- `/api/system/migration/migrate` - Migration ausführen
|
||
- `/api/system/migration/rollback` - Migration zurückrollen
|
||
|
||
---
|
||
|
||
## 📝 Erforderliche Änderungen
|
||
|
||
### 1. ALLE API-Routen prüfen und `/api` hinzufügen
|
||
|
||
**Schritt 1**: Finde alle API-Aufrufe im Frontend:
|
||
|
||
```bash
|
||
# Alle fetch/axios Aufrufe finden
|
||
grep -r "fetch\(" frontend/src/
|
||
grep -r "axios\." frontend/src/
|
||
```
|
||
|
||
**Schritt 2**: Prüfe jede Route und füge `/api` Prefix hinzu (falls fehlend):
|
||
|
||
```javascript
|
||
// ❌ FALSCH (alte Routen)
|
||
fetch('/groups/123')
|
||
fetch('/groups/123/approve')
|
||
fetch('/moderation/groups/123')
|
||
|
||
// ✅ RICHTIG (neue Routen)
|
||
fetch('/api/groups/123') // Public
|
||
fetch('/api/admin/groups/123/approve') // Admin (+ Bearer Token!)
|
||
fetch('/api/admin/groups/123') // Admin (+ Bearer Token!)
|
||
```
|
||
|
||
### 2. Admin-Session & CSRF einrichten
|
||
|
||
Die Admin-API verwendet jetzt serverseitige Sessions mit CSRF-Schutz. Statt Tokens in `.env` zu hinterlegen, erfolgt die Authentifizierung über Login-Endpunkte:
|
||
|
||
1. **Setup-Status abfragen** – `GET /auth/setup/status` → `{ needsSetup, hasSession }`
|
||
2. **Ersten Admin anlegen** – `POST /auth/setup/initial-admin` (nur einmal nötig)
|
||
3. **Login** – `POST /auth/login` mit `{ username, password }`
|
||
4. **CSRF Token holen** – `GET /auth/csrf-token` (liefert `csrfToken` und setzt HttpOnly Session-Cookie)
|
||
|
||
Alle nachfolgenden Admin-Requests senden automatisch das Session-Cookie (`credentials: 'include'`) und den `X-CSRF-Token` Header.
|
||
|
||
### 3. API-Aufrufe für Admin-Endpoints anpassen
|
||
|
||
#### Vorher (ohne Session):
|
||
```javascript
|
||
const response = await fetch('/api/admin/groups');
|
||
```
|
||
|
||
#### Nachher (mit Session + CSRF):
|
||
```javascript
|
||
const response = await fetch('/api/admin/groups', {
|
||
method: 'GET',
|
||
credentials: 'include',
|
||
headers: {
|
||
'X-CSRF-Token': csrfToken, // nur bei mutierenden Requests zwingend nötig
|
||
}
|
||
});
|
||
```
|
||
|
||
### 3. Zentrale API-Helper-Funktion erstellen
|
||
|
||
**Empfohlen**: Nutze `src/services/adminApi.js` als einzige Stelle, die Session- und CSRF-Handling kapselt:
|
||
|
||
```javascript
|
||
const SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);
|
||
let csrfToken = null;
|
||
|
||
const ensureCsrfToken = async () => {
|
||
if (!csrfToken) {
|
||
const response = await fetch('/auth/csrf-token', { credentials: 'include' });
|
||
const data = await response.json();
|
||
csrfToken = data.csrfToken;
|
||
}
|
||
return csrfToken;
|
||
};
|
||
|
||
export const adminFetch = async (url, options = {}) => {
|
||
const method = (options.method || 'GET').toUpperCase();
|
||
const headers = new Headers(options.headers || {});
|
||
|
||
if (!SAFE_METHODS.has(method)) {
|
||
headers.set('X-CSRF-Token', await ensureCsrfToken());
|
||
}
|
||
|
||
const response = await fetch(url, {
|
||
...options,
|
||
method,
|
||
credentials: 'include',
|
||
headers
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw await parseError(response);
|
||
}
|
||
|
||
return response;
|
||
};
|
||
```
|
||
|
||
### 4. Error Handling erweitern
|
||
|
||
```javascript
|
||
try {
|
||
const response = await adminFetch('/api/admin/groups');
|
||
const data = await response.json();
|
||
// ...
|
||
} catch (error) {
|
||
if (error.status === 401) {
|
||
// Session abgelaufen
|
||
redirectToLogin();
|
||
} else if (error.status === 403 && error.reason === 'CSRF_INVALID') {
|
||
// CSRF neu anfordern
|
||
await adminSession.refreshStatus();
|
||
} else if (error.status === 429) {
|
||
notifyRateLimit();
|
||
} else {
|
||
console.error('Admin API error:', error);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🔍 Betroffene Dateien finden
|
||
|
||
### Alle API-Calls prüfen (KRITISCH!)
|
||
|
||
```bash
|
||
cd frontend/src
|
||
|
||
# ALLE API-Calls finden (fetch + axios):
|
||
grep -rn "fetch(" --include="*.js" --include="*.jsx"
|
||
grep -rn "axios\." --include="*.js" --include="*.jsx"
|
||
|
||
# Spezifisch nach alten Routen OHNE /api suchen:
|
||
grep -rn "fetch('/groups" --include="*.js"
|
||
grep -rn "fetch('/moderation" --include="*.js"
|
||
|
||
# Admin-API-Calls finden:
|
||
grep -rn "/api/admin" --include="*.js" --include="*.jsx"
|
||
```
|
||
|
||
**Bekannte betroffene Dateien:**
|
||
|
||
### Routen ohne `/api` Prefix (MÜSSEN GEFIXT WERDEN):
|
||
- `Components/Pages/ModerationGroupsPage.js`
|
||
- ❌ `/groups/${groupId}/approve` → ✅ `/api/admin/groups/${groupId}/approve`
|
||
- ❌ `/groups/${groupId}` (DELETE) → ✅ `/api/admin/groups/${groupId}`
|
||
- ✅ `/api/admin/social-media/platforms` für Moderationsfilter
|
||
- ✅ `/api/social-media/platforms` für öffentliche Formulare (keine Session nötig)
|
||
|
||
- `Components/Pages/ModerationGroupImagesPage.js`
|
||
- ❌ `/moderation/groups/${groupId}` → ✅ `/api/admin/groups/${groupId}`
|
||
|
||
- `Components/Pages/PublicGroupImagesPage.js`
|
||
- ❌ `/groups/${groupId}` → ✅ `/api/groups/${groupId}`
|
||
|
||
### Admin-Endpoints (Session + CSRF erforderlich):
|
||
- `Components/Pages/ModerationGroupsPage.js` - Alle Moderations-Calls
|
||
- `Components/Pages/ModerationGroupImagesPage.js` - Gruppe laden + Bilder löschen
|
||
- `Components/ComponentUtils/DeletionLogSection.js` - Deletion Log
|
||
- `Components/ComponentUtils/ConsentManager.js` - Consent-Export (Admin)
|
||
- `services/reorderService.js` - Admin-Reorder (falls im Einsatz)
|
||
|
||
### Public/Management Endpoints (nur Pfad prüfen):
|
||
- `Utils/batchUpload.js` - Bereits korrekt (`/api/...`)
|
||
- `Components/Pages/ManagementPortalPage.js` - Bereits korrekt (`/api/manage/...`)
|
||
- `Utils/sendRequest.js` - Bereits korrekt (axios)
|
||
|
||
---
|
||
|
||
## ✅ Checkliste
|
||
|
||
### Phase 1: Route-Prefixes (ALLE Dateien)
|
||
- [ ] Alle `fetch()` und `axios` Calls gefunden (grep)
|
||
- [ ] Alle Routen ohne `/api` Prefix identifiziert
|
||
- [ ] `/api` Prefix zu Public-Routen hinzugefügt (`/api/groups`, `/api/upload`)
|
||
- [ ] Admin-Routen auf `/api/admin/*` geändert
|
||
- [ ] Management-Routen auf `/api/manage/*` geprüft (sollten schon korrekt sein)
|
||
|
||
### Phase 2: Admin Authentication (Session)
|
||
- [ ] `AdminSessionProvider` wrappt die App
|
||
- [ ] `AdminSessionGate` schützt alle Moderationsseiten
|
||
- [ ] `adminApi.js` nutzt `credentials: 'include'` + `X-CSRF-Token`
|
||
- [ ] Login- und Initial-Setup-Formulare eingebunden
|
||
- [ ] Fehlerbehandlung für `401/403 (SESSION_REQUIRED/CSRF_INVALID)` ergänzt
|
||
|
||
### Phase 3: Testing & Deployment
|
||
- [ ] Frontend lokal getestet (alle Routen)
|
||
- [ ] Admin-Funktionen getestet (Approve, Delete, etc.)
|
||
- [ ] Public-Routen getestet (Gruppe laden, Upload)
|
||
- [ ] Production `.env` aktualisiert
|
||
|
||
---
|
||
|
||
## 🧪 Testing
|
||
|
||
### Lokales Testing
|
||
|
||
1. Backend starten (`npm run dev`) – stellt Session- & Auth-Routen bereit.
|
||
2. Frontend starten (`npm start`).
|
||
3. `/moderation` öffnen:
|
||
- **Falls kein Admin existiert** → Setup-Formular ausfüllen.
|
||
- Danach mit frisch erstellten Credentials anmelden.
|
||
4. Moderationsfunktionen (Approve/Delete/Reorder/Consent-Export) durchspielen.
|
||
|
||
### Test-Fälle
|
||
|
||
- ✅ Moderation funktioniert mit aktiver Session
|
||
- ✅ Login/Logout ändert sofort den Zugriff auf Seiten
|
||
- ✅ CSRF-geschützte Aktionen schlagen fehl, wenn Token manipuliert wird
|
||
- ✅ Consent-Export & Reorder funktionieren weiterhin
|
||
- ✅ Öffentliche Routen bleiben ohne Login erreichbar
|
||
|
||
---
|
||
|
||
## 📚 Weitere Dokumentation
|
||
|
||
- **Backend Auth-Doku**: `AUTHENTICATION.md`
|
||
- **API Route-Übersicht**: `backend/src/routes/README.md`
|
||
- **Route-Konfiguration**: `backend/src/routes/routeMappings.js`
|
||
- **OpenAPI Spec**: `backend/docs/openapi.json`
|
||
- **Swagger UI**: http://localhost:5001/api/docs/ (dev only)
|
||
|
||
---
|
||
|
||
## 🆘 Troubleshooting
|
||
|
||
### Problem: "Session Required" / 403 Fehler
|
||
|
||
**Ursachen:**
|
||
1. Session abgelaufen (Inaktivität, Browser geschlossen)
|
||
2. Cookies blockiert (Third-Party/SameSite Einstellungen)
|
||
|
||
**Lösung:**
|
||
- Seite neu laden → Login-Formular erscheint
|
||
- Browser-Einstellungen prüfen: Cookies für Host erlauben
|
||
|
||
### Problem: "CSRF invalid"
|
||
|
||
**Ursachen:**
|
||
- CSRF-Token nicht gesetzt oder veraltet
|
||
|
||
**Lösung:**
|
||
- `AdminSessionGate` neu laden → holt automatisch neues Token
|
||
- Sicherstellen, dass `adminApi` bei mutierenden Calls `X-CSRF-Token` setzt
|
||
|
||
### Problem: Setup-Formular erscheint nicht
|
||
|
||
**Ursachen:**
|
||
- Bereits ein Admin vorhanden
|
||
|
||
**Lösung:**
|
||
- Bestehende Admin-Credentials verwenden
|
||
- Falls vergessen: über Datenbank (Tabelle `admin_users`) neuen Admin eintragen oder Passwort zurücksetzen
|
||
|
||
### Problem: Login schlägt wiederholt fehl
|
||
|
||
**Checks:**
|
||
1. Backend-Logs prüfen (Rate-Limits? falsches Passwort?)
|
||
2. Prüfen, ob `ADMIN_SESSION_SECRET` gesetzt ist (sonst keine stabilen Sessions)
|
||
3. Browser-Konsole → Network Request `POST /auth/login` analysieren
|
||
|
||
---
|
||
|
||
## 🚀 Deployment
|
||
|
||
### Production Checklist
|
||
|
||
- [ ] Sicheres `ADMIN_SESSION_SECRET` (>= 32 random bytes) gesetzt
|
||
- [ ] HTTPS aktiviert (Cookies: `Secure`, `SameSite=Strict`)
|
||
- [ ] Session-DB Pfad (`ADMIN_SESSION_DIR`/`ADMIN_SESSION_DB`) persistent gemacht
|
||
- [ ] Admin-Benutzer erstellt und dokumentiert (kein Secret im Frontend)
|
||
- [ ] Monitoring/Alerting für fehlgeschlagene Logins eingerichtet
|
||
|
||
### Docker Deployment
|
||
|
||
```yaml
|
||
# docker-compose.yml
|
||
services:
|
||
backend:
|
||
environment:
|
||
- ADMIN_SESSION_SECRET=${ADMIN_SESSION_SECRET}
|
||
- ADMIN_SESSION_DIR=/data/sessions
|
||
# optional weitere Backend-ENV Variablen
|
||
frontend:
|
||
environment:
|
||
- PUBLIC_URL=${PUBLIC_URL:-/}
|
||
```
|
||
|
||
```bash
|
||
# .env (nicht in Git!)
|
||
ADMIN_SESSION_SECRET=$(openssl rand -hex 32)
|
||
```
|
||
|
||
---
|
||
|
||
**Fragen?** Siehe `AUTHENTICATION.md` für detaillierte Backend-Dokumentation.
|
||
|
||
**Status der Backend-Changes:** ✅ Vollständig implementiert und getestet (45/45 Tests passing)
|