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

99 lines
7.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).