- docker-compose.coolify(.prebuilt).yml: Service craftvia-worker (Target worker, Chromium, shm_size 1gb, gleiche Härtung), Craftvia-Variablen für app und worker. - Dockerfile: worker-Stage mit HOME=/home/app (Chromium-Profil für non-root); lokaler docker build der Targets runner und worker erfolgreich, PDF-Erzeugung im Image geprüft. - .env.example/.env.prod.example/.env.coolify.example: alle Craftvia-Variablen inkl. RLS, KI-Provider, PDF_CHROMIUM_PATH, OFFLINE_MAX_DAYS, API_RATE_LIMIT_*, AI_GENERATION_RETENTION_DAYS, AI_MONTHLY_TOKEN_LIMIT. - CI (.github, .gitea): Job gate mit Postgres (pgvector) und Redis als Service: migrate deploy, seed, Passwort für craftvia_app, tsc, lint, build, npm run test. - docs/craftvia/DEPLOY.md (aus den Certvia-Deploy-Docs abgeleitet): Architektur, Domains, Secrets, Worker, Migrationen, RLS-Aktivierung, Backup/Restore, KI, Rate Limits, Aufbewahrung, Smoke, Update/Rollback. build-and-push-images.sh baut craftvia-worker. - Certvia-/ISMS-Dokumente aus docs/ nach docs/_certvia-archiv/ (mit README); Verweise in README.md und Skript-Kommentaren angepasst. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7.9 KiB
SEC2 — Passwort-Reset, Passwort ändern, E-Mail-Änderung & Sessions (Detail-Prompt)
Claude-Code-Prompt für einen Entwickler. Basis
dev→ Branchdev/sec2-auth-selfservice(PR-Zieldev). Abhängigkeit: SEC1 (Mail-Templatespassword_reset,password_changed,email_change_verify). Ziel: sicherer Self-Service — Passwort vergessen/zurücksetzen, Passwort selbst ändern, E-Mail-Adresse ändern (verifiziert) — plus ein wiederverwendbarer Session-Invalidierungs-Baustein. Gilt für Mandanten-Nutzer (/login) und Plattform-Admins (/platform/login).
Vorhandenes wiederverwenden
src/lib/password-policy.ts(Validierung, client-safe) +src/server/password.ts(Argon2id + Generator). Nicht neu implementieren.- Zwei NextAuth-Instanzen:
src/server/auth.ts(Mandant) ·src/server/platform-auth.ts(Plattform). Force-Change-Gate im(app)-Layout als Muster. - Konto-Lockout (Tenant) +
User.mustChangePasswordvorhanden. Audit-Log (scope=platformfür Plattform-Ereignisse). SEC1-Mailservice (enqueueMail).
1. Token-Modell (single-use, gehasht)
AuthToken (eigenes Modell, nicht im JWT):
id, principalType (tenant_user | platform_admin), principalId, tenantId? (nur bei tenant_user, für RLS), type (password_reset | email_change), tokenHash (SHA-256 des Rohtokens), newEmail? (nur bei email_change), expiresAt, usedAt?, createdAt, requestIp?.
- Roh-Token = 32 Byte CSPRNG, base64url; nur im Link (nie in DB/Log). In DB nur der Hash. Lookup per Hash, konstante-Zeit-Vergleich.
- Gültigkeit kurz (Reset 30–60 Min, E-Mail-Verify 60 Min); single-use (
usedAtsetzen); alte offene Tokens desselben Typs beim Neuanfordern invalidieren. - tenant_user-Zeilen unter RLS (
tenantId); platform_admin-ZeilentenantId=null(scope=platform). Migration + RLS-DO-Block.
2. Passwort-Reset (Self-Service)
2a Anfrage — „Passwort vergessen?" auf /login und /platform/login:
- Eingabe E-Mail → immer gleiche Antwort („Falls ein Konto existiert, wurde eine E-Mail gesendet."). Kein Rückschluss auf Existenz (Enumeration-Schutz).
- Rate-Limit: je IP und je Konto (z. B. 5/Stunde); zusätzlich globaler Missbrauchsschutz.
- Existiert das Konto und ist aktiv/nicht gesperrt:
AuthToken(password_reset)erzeugen, Link"{APP_BASE_URL}/reset?token=…"per SEC1 (password_reset) senden. Deaktivierte/gesperrte Konten: keine Mail, keine Fehlermeldung.
2b Einlösung — /reset?token=…:
- Token per Hash validieren (existiert, nicht abgelaufen, nicht benutzt, Konto aktiv). Ungültig → generische Meldung + Angebot „neu anfordern".
- Neues Passwort setzen: Policy-Prüfung (
password-policy.ts) + Argon2id (password.ts). Bei Wiederverwendung: Breach-Check-Hook vorsehen (Implementierung in SEC5). - Danach:
usedAtsetzen, alle Sessions invalidieren (Abschnitt 5),mustChangePassword=false, Bestätigungs-Mail (password_changed), Audit-Eintrag (ohne Token). - Redirect zum passenden Login (
/loginbzw./platform/login).
3. Passwort selbst ändern
Profilseite (Mandanten-App und Plattform-Profil):
- Felder: aktuelles Passwort, neues, Wiederholung. Alt-Passwort verifizieren (Argon2id), Policy-Prüfung.
- Erfolg: Passwort setzen, andere Sessions abmelden (aktuelle behalten — Abschnitt 5), Bestätigungs-Mail (
password_changed), Audit. - Fehler generisch; Rate-Limit auf Alt-Passwort-Versuche.
4. E-Mail-Adresse ändern (verifiziert, Double-Opt-in)
- Nutzer gibt neue Adresse ein → Alt-Passwort bestätigen (Step-up folgt in SEC3). Prüfen, dass die neue Adresse frei ist (mandantenweit bzw. plattformweit), ohne Enumeration nach außen.
AuthToken(email_change, newEmail)erzeugen; Verifizierungslink an die NEUE Adresse (email_change_verify).- Klick auf Link: Token validieren → E-Mail aktualisieren,
usedAtsetzen; Benachrichtigung an die ALTE Adresse („Ihre E-Mail wurde geändert"); Audit. Login-Identität konsistent halten (E-Mail ist Login). - Kollisionsfall (Adresse zwischenzeitlich vergeben): sauber ablehnen.
5. Session-Invalidierung (wiederverwendbarer Baustein)
NextAuth v5 nutzt JWT (stateless) → globale Invalidierung über eine Versionsmarke:
- Feld
sessionsValidAfter(Timestamp) bzw.tokenVersionanUserundPlatformAdmin. - JWT trägt
iat/Version; im Auth-Callback bzw. Layout-Check (die DB wird ohnehin schon für Kontostatus/mustChangePasswordgeprüft) zusätzlichsessionsValidAftervergleichen → ältere Tokens sind ungültig → Abmeldung. - Bump-Auslöser: Passwort-Reset (2b), Passwort ändern (3, andere Sessions), Konto-Deaktivierung, MFA-Änderung (SEC3), E-Mail-Änderung.
- „Aktuelle Session behalten" (bei Self-Change): nach Bump das aktuelle JWT mit neuer Version neu ausstellen.
- AK: ein Bump macht bestehende Sessions serverseitig ungültig; Self-Change meldet nur die anderen ab.
- Optionaler Folgeschritt (nicht SEC2-Pflicht): „aktive Sitzungen anzeigen + einzeln abmelden" braucht Session-Records — als P1 vermerken.
6. Sicherheit (verbindlich)
- Tokens: CSPRNG, nur gehasht gespeichert, single-use, kurzlebig, konstante-Zeit-Vergleich; nie in Logs/MailLog.
- Enumeration-Schutz überall (Reset, E-Mail-Änderung): generische, einheitliche Antworten und Timing.
- Rate-Limiting an Reset-Anfrage, Reset-Einlösung, Alt-Passwort-Prüfung (IP + Konto) — bindet an den globalen Limiter aus SEC5, hier aber schon inline scharf.
- Reset/Änderung an gesperrten/deaktivierten Konten unmöglich.
- Alle Ereignisse ins Audit (wer/wann/was, ohne Secrets); kompatibel zur SEC5-Audit-Erweiterung.
- Kein Auto-Login direkt nach Reset (Nutzer meldet sich neu an) — reduziert Token-Missbrauch.
7. Akzeptanzkriterien
- „Passwort vergessen" auf
/loginund/platform/loginerzeugt (nur bei aktivem Konto) eine Reset-Mail; Antwort ist immer enumeration-neutral; Rate-Limit greift. - Reset-Link ist single-use, abgelaufen/benutzt → generische Ablehnung; nach Reset sind alle Sessions ungültig, Bestätigungs-Mail + Audit vorhanden.
- Passwort-Selbständerung mit Alt-Passwort-Prüfung + Policy; andere Sessions werden abgemeldet, aktuelle bleibt.
- E-Mail-Änderung erst nach Verifizierung der neuen Adresse wirksam; alte Adresse wird informiert.
- Token-Hashes in DB (keine Klartext-Tokens), keine Secrets in Logs.
- Funktioniert für Mandanten-Nutzer und Plattform-Admins.
8. Definition of Done
tsc → lint → build (Guard-Check) grün · neue Actions in check-module-guards.ts · AuthToken + sessionsValidAfter-Felder in Migration (+RLS-DO-Block, AuthToken in TENANT_MODELS für tenant-Zeilen) · Browser-Test aller Flows (Tenant + Plattform) gegen Mailpit · Demo-Umgebung lauffähig.
9. Testplan (Kurz)
- Reset happy path (Tenant + Plattform): Anfrage → Mail (Mailpit) → Link → neues Passwort → alte Sessions abgemeldet → Login neu.
- Enumeration: unbekannte E-Mail → identische Antwort/Timing, keine Mail.
- Token-Missbrauch: abgelaufen / bereits benutzt / manipuliert → generische Ablehnung.
- Rate-Limit: viele Anfragen → gedrosselt.
- Self-Change: falsches Alt-Passwort → Ablehnung; korrekt → andere Session (zweiter Browser) wird ungültig, aktuelle bleibt.
- E-Mail-Änderung: Verify-Link an neue Adresse nötig; alte Adresse erhält Hinweis; unbestätigt → keine Änderung.
- Deaktiviertes Konto: kein Reset möglich.
10. Bibliotheken / Bausteine
crypto (CSPRNG/SHA-256, konstante-Zeit) · vorhandene password-policy.ts/password.ts · SEC1-enqueueMail · Rate-Limit-Util (mit SEC5 teilen).
Folgepaket SEC3 nutzt den Session-Invalidierungs-Baustein (MFA-Änderung) und ergänzt Step-up-Re-Auth für die E-Mail-Änderung/kritische Aktionen.