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>
7.9 KiB
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)
- Schritt 1 — E-Mail + Passwort → Credential der
Identityprüfen (Lockout + konstante Laufzeit gegen Enumeration wie heute). - Zwischenzustand: kurzlebiger, einzweckiger „MFA-pending"-Token serverseitig — erlaubt ausschließlich den MFA-Abschluss, keine App-Session. (Verhindert „halb angemeldet"-Bypass.)
- 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.
- Volle Session ausstellen (Identity authentifiziert + MFA erfüllt).
- 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:
- Server-autoritativer Kontext: aktiver
tenantIdwird bei jedem Wechsel aus einer frisch validierten Mitgliedschaft neu abgeleitet — nie aus Client-Input. RLSapp.tenant_idpro Transaktion daraus. - Re-Validierung bei jedem Wechsel/Request: Mitgliedschaft + Mandant + Nutzerstatus +
sessionsValidAfterprüfen → Entzug/Sperre wirkt sofort. - 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
IdentitystattUser; die Session trägt zusätzlich die Mitgliedschaftsliste und den aktiventenantId. - 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).