leistungsbilanz-ts/docs/authentication.md
Grovy311 c56cfb30ad Add login-required user management (admin/user roles, Config->Benutzer)
No self-signup: admins create every account in Config -> Benutzer with a
typed start password; a forced password change gates first use. Session-
cookie auth protects all /api/* routes; /api/users additionally requires
the admin role. See docs/authentication.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 19:51:47 +02:00

61 lines
2.9 KiB
Markdown
Raw 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.

# Anmeldung und Benutzerverwaltung
Die Leistungsbilanz verlangt eine Anmeldung. Es gibt kein Self-Signup: jeder
Zugang wird von einem Administrator über **Config → Benutzer** angelegt.
## Rollen
Es gibt genau zwei Rollen (`src/shared/constants/user-roles.ts`):
- **admin** – wie `user`, zusätzlich Zugriff auf den Config-Bereich
(Benutzerverwaltung, eigenes Konto)
- **user** – volle Nutzung der Leistungsbilanz (Projekte, Stromkreislisten,
Grobbilanz, …), kein Zugriff auf Config
Es gibt (bewusst, siehe unten) keine konfigurierbare Rechte-Matrix und keine
Verknüpfung mit einer Mitarbeiter-/Personen-Stammdatentabelle.
## Ablauf beim Anlegen eines Zugangs
1. Ein Administrator öffnet Config → Benutzer und legt einen neuen Zugang mit
Anmeldename, Name, Rolle und einem **Startpasswort** an, das er selbst
eintippt.
2. Der Zugang startet mit `mustChangePassword = true`.
3. Beim ersten Anmelden zeigt die Oberfläche statt der App ein Formular, das
ein eigenes Passwort verlangt, bevor es weitergeht.
4. "Passwort zurücksetzen" (ebenfalls in Config → Benutzer) setzt ein neues
Startpasswort und `mustChangePassword` wieder auf `true` – z. B. wenn
jemand sein Passwort vergessen hat.
## Technischer Aufbau
- **Datenmodell:** Tabelle `users` (`src/db/schema/users.ts`) –
`username`, `passwordHash` (scrypt, `src/server/auth/password.ts`), `name`,
`role`, `active`, `mustChangePassword`.
- **Sessions:** In-Memory-Store (`src/server/auth/sessions.ts`), Cookie
`lb_session`, sliding expiry (`AUTH_SESSION_HOURS`, Standard 12h). Ein
Neustart der API beendet alle Sessions – für einen Single-Container-Dienst
ein akzeptabler Kompromiss gegenüber einer persistierten Session-Tabelle.
- **Middleware:** `src/server/auth/auth.middleware.ts` schützt `/api/*`
(außer `/api/auth/login`, `/api/auth/status`, `/api/auth/logout`) und
erzwingt den Passwortwechsel, solange `mustChangePassword` gesetzt ist.
`/api/users/*` verlangt zusätzlich die Rolle `admin`.
- **Frontend:** `AuthProvider` lädt `/api/auth/me`; `LoginGate`
(`src/app/layout.tsx`) zeigt Login- bzw. Passwortformular, solange nicht
angemeldet bzw. `mustChangePassword` gesetzt ist, sonst die App.
## Bootstrap-Admin
`AUTH_BOOTSTRAP_USERNAME` / `AUTH_BOOTSTRAP_PASSWORT` (Env-Variablen, siehe
`compose.yaml`) legen beim ersten Start einen Admin an – **nur falls die
`users`-Tabelle noch komplett leer ist**. Danach werden diese Variablen
ignoriert; jeder weitere Zugang entsteht über Config → Benutzer. Die Werte
gehören in eine lokale, nicht versionierte `.env` auf dem Deploy-Host, nicht
in `compose.yaml` selbst.
## Bewusst nicht umgesetzt (Stand jetzt)
- Konfigurierbare Rechte-Matrix statt fixer `admin`/`user`-Rollen
- Konten-Sperre nach Fehlversuchen
- E-Mail-/Einmal-Link-Passwortvergabe, OIDC/Entra-Anmeldung
- Verknüpfung von Zugängen mit einer Mitarbeiter-Stammdatentabelle