Zum Inhalt

UC00 – Tenant-Onboarding & Schulverwaltung

Spezifikationsdatei – Leitdokument für Entwicklung, Testing und Abnahme
Stand: April 2026 | Version: 0.4 (Screen-Flow & Wireframes alle 11 Screens abgeschlossen)
Autor: David | Review: offen


Status-Übersicht

Schritt Bezeichnung Status Kommentar
1 Steckbrief ✅ Abgeschlossen v0.2 – alle Entscheide eingearbeitet
2 Screen-Flow & Wireframes ✅ Abgeschlossen Alle 11 Screens fertig (SA-01–SA-03, OB-01–OB-06, TA-01–TA-02)
3 Datenmodell 🟡 In Bearbeitung Schema-Delta definiert, Migration bereit
4 Backend (NestJS) ⬜ Offen
5 Prisma / Supabase ⬜ Offen
6 Frontend (React) ⬜ Offen
7 Testing ⬜ Offen

Legende: ✅ Abgeschlossen | 🟡 In Bearbeitung | 🔴 Blockiert | ⬜ Offen


1. Steckbrief

1.1 Übersicht

Feld Inhalt
UC-ID UC00
Name Tenant-Onboarding & Schulverwaltung
Version V1
Onboarding-Modell Option C1 – Hybrid (Superadmin + Tenant-Admin, 72h Token)
Zielgruppe Plattform-Administration
Primärakteur superadmin
Sekundärakteur tenant_admin (Schulinhaber)
Priorität Hoch – Voraussetzung für alle anderen UseCases

1.2 Ziel

Der Superadmin legt eine neue Tauchschule (Tenant) in der Plattform an und sendet dem Schulinhaber einen Einladungslink. Der Schulinhaber vervollständigt beim Erstlogin sein Schulprofil, setzt sein Passwort und richtet 2FA ein. Danach ist der Tenant aktiv und einsatzbereit.

1.3 Vorbedingungen

# Bedingung
V1 Superadmin ist eingeloggt und hat 2FA aktiv
V2 E-Mail-Adresse des Schulinhabers liegt vor
V3 Plan-Typ wurde mit dem Schulinhaber vereinbart (Starter / Professional / Enterprise)
V4 E-Mail-Versand (SMTP / Resend) ist konfiguriert

1.4 Nachbedingungen (Erfolgsfall)

# Bedingung
N1 Tenant-Datensatz ist in der DB angelegt (Status: aktiv)
N2 Tenant-Admin-User ist angelegt (Status: aktiv, 2FA aktiv)
N3 Schulprofil ist vollständig ausgefüllt (alle Pflichtfelder)
N4 Invitation-Token ist als verwendet markiert (used_at gesetzt)
N5 Recovery-Codes wurden generiert und dem Tenant-Admin angezeigt
N6 Erster Login des Tenant-Admins ist in auth_log protokolliert

2. Ablaufbeschreibung

2.1 Happy Path (Normalfall)

SUPERADMIN-TEIL:
─────────────────────────────────────────────────────────
1. Superadmin öffnet Superadmin-Panel → "Neuer Tenant"
2. Füllt Basis-Formular aus:
   – Schulname (Pflicht)
   – Land: DE / CH / AT (Pflicht)
   – Plan-Typ: Starter / Professional / Enterprise (Pflicht)
   – E-Mail des Schulinhabers (Pflicht) → wird Tenant-Admin
   – Optional: Adresse, PLZ, Stadt
3. Klickt "Tenant anlegen & Einladung senden"
4. System:
   – Generiert 8-stelligen Zufalls-Slug (z.B. "k7x2m9pq")
   – Legt tenants-Datensatz an (is_active = TRUE)
   – Legt users-Datensatz an (Rolle: tenant_admin, is_active = FALSE)
   – Erstellt invitation_token (token_type = 'admin', expires_at = +72h)
   – Sendet Einladungs-E-Mail via Template 'invite_admin' / 'de'
5. Superadmin sieht Bestätigung: "Einladung gesendet an [E-Mail]"

TENANT-ADMIN-TEIL (Schulinhaber):
─────────────────────────────────────────────────────────
6.  Schulinhaber öffnet Einladungslink aus E-Mail
7.  System prüft: Token gültig + nicht abgelaufen + nicht verwendet
8.  Schulinhaber setzt Passwort:
    – Plattform-Minimum (unveränderlich): 12 Zeichen,
      Gross + Klein + Zahl + Sonderzeichen
    – Inline-Validierung während Eingabe
9.  Schulinhaber richtet 2FA ein (TOTP, z.B. Google Authenticator):
    – QR-Code wird angezeigt
    – Bestätigung mit erstem Code erforderlich
10. System generiert 8 Recovery-Codes:
    – Werden einmalig im Klartext angezeigt
    – Schulinhaber muss bestätigen ("Codes gesichert")
    – Codes werden als bcrypt-Hash in recovery_codes gespeichert
11. System aktiviert User-Account (is_active = TRUE)
12. Schulinhaber wird zu Schulprofil-Formular weitergeleitet.
    Pflichtfelder (*):
    – Schulname* (vorausgefüllt, änderbar)
    – Strasse, PLZ*, Stadt*
    – Telefon (fest oder mobil, mind. eines Pflicht*)
    – Kontaktperson*
    – Kontakt-E-Mail*
    – Standard-Währung: CHF / EUR*
    Optionale Felder:
    – MwSt-Nummer (je nach Land)
    – Logo (V2)
13. Schulinhaber speichert Schulprofil
14. System:
    – Speichert Schulprofil-Felder in tenants
    – Markiert invitation_token als verwendet (used_at = NOW())
    – Schreibt auth_log-Eintrag (event = 'first_login')
15. Schulinhaber wird zum Dashboard weitergeleitet

2.2 Alternative Pfade

ID Situation Ablauf
A1 Superadmin bricht Formular ab Kein Datensatz wird angelegt, kein Token erstellt
A2 Schulinhaber öffnet Link, schliesst Browser vor Abschluss Token bleibt gültig, Schulinhaber kann Link erneut öffnen (solange < 72h)
A3 Schulinhaber schliesst Browser nach Passwort/2FA, vor Schulprofil Account ist aktiv, Schulprofil unvollständig → beim nächsten Login direkt zum Schulprofil
A4 Superadmin möchte Einladung erneut senden Klick "Einladung erneut senden" → alter Token invalidiert, neuer Token (+72h) erstellt und gesendet
A5 Tenant-Admin verliert 2FA-Gerät, hat Recovery-Codes Recovery-Code eingeben → einmalig gültig, danach neue 2FA einrichten
A6 Tenant-Admin verliert 2FA-Gerät, hat keine Recovery-Codes Superadmin setzt 2FA zurück → E-Mail-Benachrichtigung via Template '2fa_reset' / 'de'

2.3 Fehlerfälle

ID Fehler Verhalten
F1 E-Mail-Adresse bereits vergeben Formular-Fehler: "Diese E-Mail ist bereits registriert"
F2 Slug-Kollision (extrem unwahrscheinlich) System generiert automatisch neuen Zufalls-Slug
F3 Einladungslink abgelaufen (> 72h) Fehlermeldung: "Dieser Link ist abgelaufen. Bitte kontaktiere die Tauchschule."
F4 Einladungslink bereits verwendet Fehlermeldung: "Dieser Link wurde bereits verwendet."
F5 E-Mail-Versand schlägt fehl Fehlermeldung für Superadmin, Tenant wird NICHT angelegt, system_log-Eintrag
F6 Passwort erfüllt Plattform-Minimum nicht Inline-Validierung mit konkretem Hinweis
F7 2FA-Code falsch Max. 5 Versuche, dann 15 Minuten Sperre, auth_log-Eintrag
F8 Recovery-Code falsch oder bereits verwendet Fehlermeldung, auth_log-Eintrag

3. Geschäftsregeln

ID Regel
G1 Jede E-Mail-Adresse kann nur einmal als Tenant-Admin registriert werden
G2 Invitation-Token läuft nach 72 Stunden ab (Modell C1)
G3 2FA ist für tenant_admin Pflicht und kann nicht deaktiviert werden
G4 Passwort Plattform-Minimum (hardcodiert, unveränderlich): 12 Zeichen, Gross + Klein + Zahl + Sonderzeichen
G5 Tenant-Admin kann in V2 eine strengere Passwort-Policy setzen (nie unterhalb Plattform-Minimum)
G6 Schulprofil muss vollständig sein (alle Pflichtfelder) bevor Mitarbeiter eingeladen werden können
G7 Ein Tenant kann nur vom Superadmin angelegt werden (kein Self-Service in V1)
G8 Der Slug ist ein 8-stelliger Zufallsstring (alphanumerisch, Kleinbuchstaben) — keine Rückschlüsse auf Kundendaten
G9 Standard-Währung kann nach dem ersten Speichern nur durch Superadmin geändert werden
G10 Recovery-Codes werden als bcrypt-Hash gespeichert, sind nur einmalig verwendbar
G11 Recovery-Codes werden nur einmalig im Klartext angezeigt — danach nicht mehr abrufbar
G12 Superadmin-Reset von 2FA löst E-Mail-Benachrichtigung an Tenant-Admin aus
G13 Tenant kann nur deaktiviert werden (is_active = FALSE), nicht gelöscht (V1)
G14 E-Mail-Templates sind in der DB hinterlegt (notification_templates), V1 nur DE

4. Betroffene Datenbank-Tabellen

Tabelle Operation Felder
tenants INSERT slug (Zufalls-8-Zeichen), name, country, plan_type, is_active = TRUE
tenants UPDATE Schulprofil-Felder (Schritt 12–13)
users INSERT tenant_id, email, role = tenant_admin, is_active = FALSE
users UPDATE password_hash, tfa_secret, tfa_enabled = TRUE, is_active = TRUE
invitation_tokens INSERT tenant_id, user_id, token_type = 'admin', invited_by, invited_email, expires_at = +72h
invitation_tokens UPDATE used_at = NOW() (bei Abschluss)
recovery_codes INSERT 8 Datensätze pro User (code_hash, user_id, tenant_id)
recovery_codes UPDATE used_at = NOW() (bei Einlösung)
notification_templates READ Template invite_admin / de für E-Mail-Versand
notification_templates READ Template 2fa_reset / de bei Superadmin-Reset
auth_log INSERT Erster Login, 2FA-Events, Recovery-Code-Nutzung
audit_log INSERT Alle Datenänderungen
system_log INSERT E-Mail-Fehler (F5)

5. API-Endpunkte (Planung)

Methode Endpoint Akteur Beschreibung
POST /superadmin/tenants superadmin Tenant + Tenant-Admin anlegen, Einladung senden
GET /superadmin/tenants superadmin Alle Tenants auflisten
GET /superadmin/tenants/:slug superadmin Tenant-Details
PATCH /superadmin/tenants/:slug/status superadmin Tenant aktivieren / deaktivieren
POST /superadmin/tenants/:slug/resend-invite superadmin Einladung erneut senden
POST /superadmin/users/:id/reset-2fa superadmin 2FA eines Users zurücksetzen
GET /auth/invite/:token öffentlich Token validieren
POST /auth/invite/:token/accept öffentlich Passwort setzen + 2FA einrichten
GET /auth/invite/:token/recovery-codes öffentlich Recovery-Codes anzeigen (einmalig)
POST /auth/login alle Login mit E-Mail + Passwort + 2FA
POST /auth/login/recovery alle Login mit Recovery-Code statt 2FA
PATCH /tenants/profile tenant_admin Schulprofil speichern

6. Offene Fragen / Entscheide

ID Frage Priorität Status Entscheid
OE1 invitation_tokens: nullable + token_type Hoch Nullable + token_type ENUM, Migration ausgeführt
OE2 Slug-Generierung Mittel 8-stelliger Zufalls-Slug, keine Kundendaten in URL
OE3 E-Mail-Templates & Mehrsprachigkeit Mittel notification_templates-Tabelle, V1 nur DE, Migration ausgeführt
OE4 2FA-Gerät verloren Hoch Beide: Recovery-Codes + Superadmin-Reset
OE5 Schulprofil-Vollständigkeit Niedrig Pflichtfeld-Markierung (*) + System-Hinweis, kein Prozentsatz
OE6 Tenant deaktivieren/löschen Mittel V1 nur Deaktivieren, Löschen mit Pseudonymisierung in V2
OE7 Plan ändern auf SA-03 Mittel Separater Modal-Dialog mit Plan-Kacheln + optionaler Notiz
OE8 Superadmin-2FA-Reset auf SA-03 Mittel Im Benutzer-Abschnitt auf SA-03, Button direkt beim Admin
OE9 Onboarding-Status auf SA-03 Niedrig Status-Banner wenn Token offen oder Schulprofil unvollständig

7. Schema-Delta (UC00)

Bereits in Supabase ausgeführt:

  • invitation_tokens: customer_id nullable, +user_id, +token_type
  • notification_templates: neue Tabelle mit DE-Templates (3 Templates)

Noch auszuführen (schema-migrate-uc00.sql):

  • recovery_codes: neue Tabelle (2FA-Backup-Codes)

Neue Tabellen nach UC00:

notification_templates   ← neu (E-Mail-Templates, mehrsprachig vorbereitet)
recovery_codes           ← neu (2FA-Backup-Codes, bcrypt-gehasht)

Geänderte Tabellen:

invitation_tokens:
  customer_id: NOT NULL → nullable
  + user_id:   UUID FK → users
  + token_type: ENUM ('customer', 'admin')
  + CHECK-Constraint chk_invitation_target

8. Abhängigkeiten

Abhängigkeit Typ Status
Supabase DB verbunden (Prisma .env) Technisch ⬜ Offen
E-Mail-Service konfiguriert (SMTP / Resend) Technisch ⬜ Offen
JWT-Authentifizierung implementiert Technisch ⬜ Offen
2FA-Bibliothek (otplib) Technisch ⬜ Offen
bcrypt-Bibliothek (Recovery-Codes) Technisch ⬜ Offen
Slug-Generator (nanoid / custom) Technisch ⬜ Offen
Superadmin-User existiert in DB Daten ⬜ Offen

9. Screen-Flow & Wireframes

Status: ✅ Abgeschlossen — alle 11 Screens fertiggestellt (JSX + SVG)

9.1 Screen-Übersicht

Screen Bezeichnung Akteur Wireframe Dateien
SA-01 Tenant-Liste superadmin ✅ Abgeschlossen sa-01-tenant-liste.jsx / sa-01-tenant-liste.svg
SA-02 Neuer Tenant (Formular) superadmin ✅ Abgeschlossen sa-02-neuer-tenant.jsx / sa-02-neuer-tenant.svg
SA-03 Tenant-Detail (Profil + Status + Aktionen) superadmin ✅ Abgeschlossen sa-03-tenant-detail.jsx / sa-03-tenant-detail.svg
OB-01 Einladungslink öffnen (Token-Validierung) tenant_admin ✅ Abgeschlossen ob-01-einladungslink.jsx / ob-01-einladungslink.svg
OB-02 Passwort setzen tenant_admin ✅ Abgeschlossen ob-02-passwort-setzen.jsx / ob-02-passwort-setzen.svg
OB-03 2FA einrichten (QR-Code) tenant_admin ✅ Abgeschlossen ob-03-2fa-einrichten.jsx / ob-03-2fa-einrichten.svg
OB-04 Recovery-Codes anzeigen + bestätigen tenant_admin ✅ Abgeschlossen ob-04-recovery-codes.jsx / ob-04-recovery-codes.svg
OB-05 Schulprofil ausfüllen tenant_admin ✅ Abgeschlossen ob-05-schulprofil.jsx / ob-05-schulprofil.svg
OB-06 Dashboard (Erstanmeldung abgeschlossen) tenant_admin ✅ Abgeschlossen ob-06-dashboard.jsx / ob-06-dashboard.svg
TA-01 Schulprofil bearbeiten tenant_admin ✅ Abgeschlossen ta-01-schulprofil-bearbeiten.jsx / ta-01-schulprofil-bearbeiten.svg
TA-02 Login mit Recovery-Code tenant_admin ✅ Abgeschlossen ta-02-login-recovery-code.jsx / ta-02-login-recovery-code.svg

9.2 Wireframe-Entscheide

ID Entscheid Screen Beschreibung
OE7 Plan ändern SA-03 Separater Modal-Dialog mit Plan-Kacheln (Test / Starter / Professional / Enterprise), optionales Notizfeld, Button erst aktiv bei Planwechsel
OE8 Superadmin-2FA-Reset SA-03 Button direkt im Benutzer-Abschnitt; Modal-Dialog mit Warn-Banner (Template-Hinweis 2fa_reset/de) und Bestätigungs-Checkbox
OE9 Onboarding-Status-Banner SA-03 Gelber Banner erscheint nur wenn Token noch offen oder Schulprofil unvollständig; enthält direkten Button "Einladung erneut senden"
Deaktivieren-Dialog SA-01 / SA-03 Identischer Modal-Dialog auf beiden Screens: Pflicht-Dropdown Grund, optionaler Freitext, E-Mail-Vorschau, Button erst aktiv nach Grundauswahl
Aktions-Buttons SA-01 SA-01 Pro Zeile: Detail → SA-03 | Deaktiv./Aktivier. → Dialog | Plan ä. → SA-03
Fortschrittsleiste OB-01–OB-05 5-Schritt-Leiste (Link, Passwort, 2FA, Codes, Profil) durchläuft alle Onboarding-Screens
Token-Zustände OB-01 Drei Zustände simulierbar: gültig (Happy Path), F3 abgelaufen, F4 bereits verwendet
Vollständigkeits-Indikator OB-05 Zwei %-Werte nebeneinander: Pflichtfelder + alle Felder
Währung readonly OB-05 / TA-01 Währungs-Feld nach erstem Speichern readonly mit G9-Hinweis
Recovery-Code Zwangs-2FA TA-02 Nach erfolgreichem Login mit Recovery-Code → Zwangs-Weiterleitung zu OB-03 (neue 2FA einrichten)

9.3 Dateipfade

Alle Wireframe-Dateien liegen unter docs/developer/wireframes/:

wireframes/
  sa-01-tenant-liste.jsx      ← interaktiver Wireframe (React)     ✅
  sa-01-tenant-liste.svg      ← statischer Wireframe (GitHub/Doku) ✅
  sa-02-neuer-tenant.jsx      ← interaktiver Wireframe (React)     ✅
  sa-02-neuer-tenant.svg      ← statischer Wireframe (GitHub/Doku) ✅
  sa-03-tenant-detail.jsx     ← interaktiver Wireframe (React)     ✅
  sa-03-tenant-detail.svg     ← statischer Wireframe (GitHub/Doku) ✅
  ob-01-einladungslink.jsx    ← interaktiver Wireframe (React)     ✅
  ob-01-einladungslink.svg    ← statischer Wireframe (GitHub/Doku) ✅
  ob-02-passwort-setzen.jsx   ← interaktiver Wireframe (React)     ✅
  ob-02-passwort-setzen.svg   ← statischer Wireframe (GitHub/Doku) ✅
  ob-03-2fa-einrichten.jsx    ← interaktiver Wireframe (React)     ✅
  ob-03-2fa-einrichten.svg    ← statischer Wireframe (GitHub/Doku) ✅
  ob-04-recovery-codes.jsx    ← interaktiver Wireframe (React)     ✅
  ob-04-recovery-codes.svg    ← statischer Wireframe (GitHub/Doku) ✅
  ob-05-schulprofil.jsx       ← interaktiver Wireframe (React)     ✅
  ob-05-schulprofil.svg       ← statischer Wireframe (GitHub/Doku) ✅
  ob-06-dashboard.jsx         ← interaktiver Wireframe (React)     ✅
  ob-06-dashboard.svg         ← statischer Wireframe (GitHub/Doku) ✅
  ta-01-schulprofil-bearbeiten.jsx ← interaktiver Wireframe (React)     ✅
  ta-01-schulprofil-bearbeiten.svg ← statischer Wireframe (GitHub/Doku) ✅
  ta-02-login-recovery-code.jsx    ← interaktiver Wireframe (React)     ✅
  ta-02-login-recovery-code.svg    ← statischer Wireframe (GitHub/Doku) ✅

9.4 Wireframe-Vorschau

SA-01 Wireframe SA-02 Wireframe SA-03 Wireframe OB-01 Wireframe OB-02 Wireframe OB-03 Wireframe OB-04 Wireframe OB-05 Wireframe OB-06 Wireframe TA-01 Wireframe TA-02 Wireframe

10. Testing

Status: ⬜ Offen — folgt in Schritt 7

10.1 Test-Kategorien (Planung)

Kategorie Tool Umfang
Unit-Tests Jest TenantService, InvitationService, AuthService, SlugGenerator, RecoveryCodeService
Integration-Tests Jest + Supertest Alle API-Endpunkte aus Abschnitt 5
E2E-Tests Cypress Happy Path UC00, Fehlerfälle F3, F4, F7
Sicherheits-Tests Manuell Tenant-Isolation, Rollenprüfung, Token-Missbrauch, Recovery-Code-Replay

10.2 Pflicht-Testfälle (Planung)

ID Testfall Typ
T01 Superadmin legt Tenant an → Einladungsmail gesendet E2E
T02 Schulinhaber akzeptiert Einladung → Account aktiv, 2FA aktiv E2E
T03 Recovery-Codes werden generiert und angezeigt E2E
T04 Schulinhaber füllt Schulprofil aus → Tenant vollständig E2E
T05 Token nach 72h abgelaufen → Fehlerfall F3 Integration
T06 Token bereits verwendet → Fehlerfall F4 Integration
T07 Recovery-Code einlösen → einmalig gültig, danach ungültig Integration
T08 Superadmin-Reset 2FA → E-Mail-Benachrichtigung Integration
T09 Tenant A kann Daten von Tenant B nicht sehen Sicherheit
T10 mitarbeiter kann keinen Tenant anlegen Sicherheit
T11 Passwort unter Plattform-Minimum → Ablehnung Unit
T12 Slug ist 8 Zeichen, alphanumerisch, eindeutig Unit
T13 Mitarbeiter-Einladung blockiert wenn Schulprofil unvollständig Integration

11. Änderungshistorie

Datum Version Änderung Autor
April 2026 0.1 Initiale Version – Steckbrief David
April 2026 0.2 Alle Entscheide OE1–OE6 eingearbeitet, Schema-Delta, Recovery-Codes, Templates David
April 2026 0.3 Wireframes SA-01–SA-03 abgeschlossen (JSX + SVG); Abschnitt 9 vollständig überarbeitet: Screen-Tabelle, Entscheide OE7–OE9, Dateipfade; OE7–OE9 in Abschnitt 6 ergänzt David
April 2026 0.4 Wireframes OB-01–OB-06 + TA-01–TA-02 abgeschlossen; 9.1 Screen-Tabelle vollständig, 9.2 OB/TA-Entscheide ergänzt, 9.3 alle 22 Dateipfade eingetragen David

12. Was in CLAUDE.md übernommen wird

In CLAUDE.md übernommen:

  • UC-ID, Name und Status (Zeile in der Checkliste)
  • Neue Tabellen im DB-Überblick (notification_templates, recovery_codes)
  • Neue offene Punkte in der Nächste-Schritte-Liste

Nur in der UC-Spezifikation (nicht in CLAUDE.md):

  • Detaillierter Ablauf (Happy Path, Alternative Pfade, Fehlerfälle)
  • Geschäftsregeln
  • Offene Fragen / Entscheide und deren Begründungen
  • Screen-Identifikation und Wireframes
  • Test-Cases
  • Schema-Delta-Details
  • Abhängigkeiten
  • Änderungshistorie