fix: Login-userId war nicht stabil gegenüber Groß-/Kleinschreibung

/api/auth/login mintete die JWT-userId bisher aus dem roh eingegebenen Benutzernamen. LiteDBs
Standard-Collation vergleicht den eindeutigen Index auf Username aber case-insensitive - ein
Login mit nur einmal abweichender Schreibweise auf einem zweiten Gerät authentifiziert
erfolgreich, mintet aber eine andere userId. Da EventStore.GetCol(userId) diese direkt als
Dateiname für den Server-seitigen Event-Speicher nutzt, entstanden zwei komplett getrennte
Datenbestände für ein und dasselbe, aus Nutzersicht einzige Konto - Ursache dafür, dass ein
zweites Gerät trotz "since=0" durchgängig 0 Ereignisse erhielt.

UserStore.VerifyPassword(bool) durch Authenticate(UserEntry?) ersetzt, das bei Erfolg den
kanonisch gespeicherten Nutzereintrag liefert; /api/auth/login mintet Token und userId daraus
statt aus der Roheingabe.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 23:51:31 +02:00
co-authored by Claude Sonnet 5
parent e7e5faeba8
commit ba8786906a
4 changed files with 78 additions and 13 deletions
+27
View File
@@ -1744,6 +1744,33 @@ die Docker-Verifikation unter 10.2.4 (kein Docker im Entwicklungsstand verfügba
**Merke:** bei Git-basierten Deploy-Plattformen (Dokploy & vergleichbare) niemals Bind-Mounts
relativ zum Checkout-Verzeichnis für persistente Daten verwenden — nur Named Volumes oder ein
Pfad explizit außerhalb des von der Plattform verwalteten Checkouts sind sicher.
- [x] **10.2.5** Login-`userId` war nicht stabil gegenüber Groß-/Kleinschreibung
(Nutzer-Bug-Report nach 10.1.10: "nur ein Nutzer im System", trotzdem bekam Client 2 auch
mit `since=0` durchgängig 0 Ereignisse zurück).
**Ursache:** `/api/auth/login` mintete den JWT-`userId` bisher aus dem roh eingegebenen
`req.Username`. LiteDBs Standard-Collation vergleicht Strings (und damit auch den
eindeutigen Index auf `Username` in `UserStore`) aber standardmäßig case-insensitive — durch
einen Test bestätigt: `VerifyPassword("Sebastian", ...)` authentifiziert erfolgreich gegen
ein als `"sebastian"` angelegtes Konto. Loggt sich ein zweites Gerät mit nur EINMAL anders
getippter Groß-/Kleinschreibung ein, meldet der Login trotzdem Erfolg, mintet aber eine
ANDERE `userId` — und `EventStore.GetCol(userId)` verwendet diese direkt als Dateiname für
den Server-seitigen Event-Speicher. Ergebnis: zwei komplett getrennte, nie überlappende
Datenbestände für ein und dasselbe, aus Nutzersicht einzige Konto — Push von Gerät 1 landete
unter `"Sebastian"`, Pull von Gerät 2 fragte unter `"sebastian"` nach und fand nichts, exakt
das beobachtete Symptom.
**Fix:** `UserStore.VerifyPassword(username, password): bool` ersetzt durch
`Authenticate(username, password): UserEntry?`, das bei Erfolg den KANONISCH gespeicherten
Nutzereintrag zurückgibt statt nur `true`. `/api/auth/login` mintet Token und `userId` jetzt
aus `user.Username` (dem gespeicherten Namen), nicht mehr aus `req.Username` — die
Groß-/Kleinschreibung beim Login hat damit keinen Einfluss mehr auf die serverseitige
Datenablage. Neuer Regressionstest
`UserStoreTests.Authenticate_AndereGrossKleinschreibung_LiefertDenKanonischGespeichertenNamen`.
**Betroffene Geräte:** einmal neu anmelden (Logout/Login in den Sync-Einstellungen) reicht,
damit beide Geräte fortan dieselbe `userId` verwenden — zusätzlich auf dem zuvor
"abgehängten" Gerät einmal "Vollständigen Sync erzwingen" (10.1.10) nutzen, damit es den
unter der korrekten `userId` bereits vorhandenen Bestand nachlädt.
### 10.3 Verschlüsselung
- [x] **10.3.1** Schlüsselübertragung auf ein zweites Gerät (QR-Code oder Passphrase).