Project-Image-Uploader/frontend/MIGRATION-GUIDE.md
matthias.lotz 6332b82c6a Feature Request: admin session security
- 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
2025-11-23 21:18:42 +01:00

11 KiB
Raw Permalink Blame History

Frontend Migration Guide - API Umstrukturierung

Datum: 16. November 2025
Betrifft: ALLE API-Aufrufe im Frontend
Status: ⚠️ Aktion erforderlich - ALLE Routen prüfen!


<EFBFBD> BREAKING CHANGE: Konsistente /api Prefixes

ALLE API-Routen haben sich geändert!

Vorher (inkonsistent):

// 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):

// 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:

# 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):

// ❌ 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):

const response = await fetch('/api/admin/groups');

Nachher (mit Session + CSRF):

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:

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

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!)

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.
  1. 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

# 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:-/}
# .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)