Files
craftvia/docs/KONZEPT-identity-mandanten.md
msolarczekandClaude Opus 5 c8e6f30a27
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s
Basis: Certvia dev@a48c5fb als Fundament für Craftvia
Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation
und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 11:05:39 +02:00

7.9 KiB
Raw Permalink Blame History

Umsetzungskonzept — Zentrale Identität mit Mandanten-Mitgliedschaften (Option C)

Status: Entscheidungsvorlage (ohne Code). Ziel: eine Person meldet sich mit einem Login/Passwort und einer MFA an und wählt anschließend, in welchem Mandanten sie arbeitet — mit je Mandant unterschiedlichen Rollen. Die mandantengetrennte Datenhaltung (RLS, Ownership) bleibt unangetastet.

1. Grundmodell in einem Satz

Eine globale Identity (E-Mail + ein Passwort + eine MFA) verweist auf N per-Mandant-User-Zeilen („Mitgliedschaften", jede mit eigenen Rollen). Zentralisiert werden nur Anmeldung + MFA + Mandantenwechsel; alles Fachliche bleibt pro Mandant.

2. Getroffene Entscheidungen

# Entscheidung Festlegung
A MFA-Zeitpunkt Beim Login, als getrennter zweiter Schritt (Two-Step, „identifier-first")
B Reset-Hoheit Mandanten-Admin verliert MFA-/Passwort-Reset; Reset via Self-Service (Recovery-Codes) + Plattform-Ebene. Mandanten-Admin entzieht nur die Mitgliedschaft.
C Nutzeranlage Einladung/Bestätigung als Standard (Kunden-Dashboard zwingend); Plattform-Admin darf zusätzlich direkt verknüpfen
D Migration Altbestand Entfällt — bisher nur Testdaten. Neuanlage/Seed statt Zusammenführung.
E Plattform-Admins Getrennter Store (platform_admins) bleibt — eigene Sicherheitsdomäne
+ Login-Fluss MFA-Abfrage erst nach Benutzername + Passwort (siehe §5)
+ Passwort-Policy Eine globale Baseline, passphrasen-freundlich (NIST 800-63B), mind. so streng wie der strengste Mandant

Phase 2 (bewusst zurückgestellt): per-Mandant-Schalter „bei jedem Betreten Step-up erzwingen"; E-Mail-Änderung als Identity-Operation.

3. Datenmodell-Skizze (konzeptuell)

Neu: Identity (global) — die Anmelde-Identität einer Person.

  • email (global eindeutig, Verknüpfungsschlüssel) · passwordHash · MFA (mfaSecret, mfaEnrolledAt, recoveryCodes) · status · sessionsValidAfter (globaler Kill-Switch) · Lockout-Felder.

User (bestehend, bleibt pro Mandant) = „Mitgliedschaft" — bekommt nur ein neues Feld identityId → Identity.

  • Behält: tenantId, name, status, Rollen (user_roles), alle Ownership-FKs (Asset-/Risk-/Prozess-Eigentümer …).
  • Gibt ab (wandert auf Identity): passwordHash, MFA-Felder, Recovery-Codes → künftig nicht mehr für Auth genutzt.

Verknüpfung: Identity 1 —— N User(=Membership). Eine Person mit drei Mandanten = eine Identity + drei User-Zeilen.

Unverändert: Role/UserRole/Permission (pro Mandant), Tenant, TenantSettings (inkl. MFA-Pflicht-Flag), platform_admins (separat).

4. Was liegt wo?

Aspekt Identity (global) Membership = User (pro Mandant)
Passwort / Passphrase ✅ (eins) —
MFA / Recovery-Codes ✅ (eine Einrichtung) —
Rollen & Rechte — ✅ (je Mandant frei verschieden)
Ownership (Assets, Risiken …) — ✅
Sperre der Person (global) ✅ status/sessionsValidAfter —
Entzug des Zugangs zu einem Mandanten — ✅ Mitgliedschaft deaktivieren/löschen

5. Login-Fluss (Two-Step, MFA beim Login)

  1. Schritt 1 — E-Mail + Passwort → Credential der Identity prüfen (Lockout + konstante Laufzeit gegen Enumeration wie heute).
  2. Zwischenzustand: kurzlebiger, einzweckiger „MFA-pending"-Token serverseitig — erlaubt ausschließlich den MFA-Abschluss, keine App-Session. (Verhindert „halb angemeldet"-Bypass.)
  3. Schritt 2 — MFA (eigene Seite), angezeigt wenn: Identity hat MFA oder ≥ 1 Mitgliedschaft in einem MFA-Pflicht-Mandanten.
    • MFA vorhanden → TOTP/Passkey verifizieren.
    • MFA nötig, aber nicht eingerichtet → jetzt einrichten (Enroll-Gate).
    • Keine MFA nötig → überspringen.
  4. Volle Session ausstellen (Identity authentifiziert + MFA erfüllt).
  5. Mandanten-Auswahl: aktive Mitgliedschaften in aktiven Mandanten. Auswahl → Server setzt tenantId + Rollen. Nur eine Mitgliedschaft → Auswahl überspringen.
    • Sicherheitsnetz: verlangt der gewählte Mandant MFA und ist sie (noch) nicht erfüllt → MFA vor Betreten nachziehen.

6. Mandantenwechsel (ohne erneuten Login) — Sicherheitskontrollen

Zulässig und Standard, aber nur mit diesen drei Pflicht-Kontrollen:

  1. Server-autoritativer Kontext: aktiver tenantId wird bei jedem Wechsel aus einer frisch validierten Mitgliedschaft neu abgeleitet — nie aus Client-Input. RLS app.tenant_id pro Transaktion daraus.
  2. Re-Validierung bei jedem Wechsel/Request: Mitgliedschaft + Mandant + Nutzerstatus + sessionsValidAfter prüfen → Entzug/Sperre wirkt sofort.
  3. MFA-Netz beim Betreten eines Pflicht-Mandanten (Backstop zu §5.5).
  • Zusätzlich: Wechsel wird auditiert („Identity X → Mandant Y"), Folgeaktionen werden der aktiven Mitgliedschaft/Mandant zugeordnet.
  • Restrisiko bewusst: breiterer Blast-Radius eines kompromittierten Credentials → beherrscht über starke Identität + MFA + kurze Session-Laufzeit + globalen Kill-Switch.

7. MFA-Matrix (Person × Mandant)

Identity hat MFA? Zielmandant verlangt MFA? Ergebnis beim Login/Betreten
ja ja Faktor verifizieren
ja nein kein Zwang (Faktor liegt vor, wird nicht abgefragt)
nein ja Einrichtung erzwingen
nein nein keine MFA

Grenze: Ein Mandant kann kein eigenes, isoliertes MFA-Gerät erzwingen — es gibt einen Faktor pro Person. (Preis der zentralen Identität.)

8. Nutzeranlage

Über das Plattform-(Admin-)Dashboard:

  • E-Mail unbekannt → neue Identity (Initialpasswort/Einladung, mustChangePassword) + Mitgliedschaft im Zielmandanten mit Rollen.
  • E-Mail bekannt → kein neues Passwort/MFA, nur Mitgliedschaft ergänzen. Person sieht den Mandanten künftig in der Auswahl. (Betreiber darf direkt verknüpfen.)

Über das Kunden-(Mandanten-)Dashboard — immer per Einladung:

  • Mandanten-Admin sieht neutral „Einladung an E-Mail gesendet" — unabhängig davon, ob die Identity schon existierte (kein Cross-Tenant-Leak / keine Konten-Enumeration).
  • Identity existiert → Person bestätigt den Beitritt in ihrem bestehenden Konto (Einwilligung durch den Menschen, nicht durch den fremden Admin).
  • Identity existiert nicht → normaler „Konto einrichten + beitreten"-Flow.
  • Rollen werden bei der Einladung je Mandant vergeben (frei verschieden pro Mandant).

9. Passwort-/Passphrasen-Policy

  • Eine globale Baseline (weil ein Credential), mind. so streng wie der strengste Mandant.
  • Passphrasen voll unterstützt: lange Obergrenze (≥ 64 Zeichen), Leer-/Unicode-Zeichen erlaubt, keine erzwungene Zusammensetzung, kein Zwangswechsel, Abgleich gegen Leak-Listen. Hashing wie bisher Argon2id.

10. Auswirkungen auf Bestehendes

  • Zwei Auth-Instanzen bleiben (Mandant vs. Plattform). Die Mandanten-Instanz authentifiziert künftig gegen Identity statt User; die Session trägt zusätzlich die Mitgliedschaftsliste und den aktiven tenantId.
  • RLS/Ownership/Audit unverändert — hängen weiter an der per-Mandant-User-Zeile.
  • Login-UI wird zweistufig (Passwort-Seite → MFA-Seite → Mandanten-Auswahl); die heutige „alles in einem Formular"-Maske entfällt.
  • MFA-/Passwort-Reset in der Mandanten-Nutzerverwaltung entfällt (→ Self-Service/Plattform); dort bleibt „Mitgliedschaft deaktivieren/Rollen ändern".
  • Bootstrap/Seed legt künftig Identity + Mitgliedschaft(en) an.

11. Offene Detailpunkte fürs Implementierungs-Feindesign (kein Blocker)

  • Genaue Lebensdauer/Signierung des „MFA-pending"-Zustands.
  • Darstellung & Default der Mandanten-Auswahl (zuletzt genutzt merken?).
  • Wortlaut der neutralen Einladungs-Rückmeldung + Ablauf/Frist der Einladung.
  • Recovery-Code-Fluss als alleiniger Selbst-Reset-Weg (Anzahl, Nachgenerierung).