User-Konzept: Auth, Sync, WebSocket-Push & Admin-UI #57

Closed
opened 2026-05-16 16:03:17 +00:00 by jreinemann-euris · 0 comments
jreinemann-euris commented 2026-05-16 16:03:17 +00:00 (Migrated from github.com)

Ziel

User-Konzept einführen: Username/Passwort-basierte Authentifizierung, user-spezifische Inventare, laufende Synchronisation, WebSocket-Push-Benachrichtigungen und eine minimale Admin-Web-UI zur Benutzerverwaltung.


Architektur-Überblick

┌─────────────┐      HTTPS + JWT        ┌──────────────┐
│  Android App │◄──────────────────────►│  Ktor Server  │
│             │      WebSocket (wss)    │              │
│  - Login    │◄───── Push-Events ──────│  - Auth      │
│  - Sync     │                        │  - REST API  │
│  - Offline  │                        │  - WebSocket │
└─────────────┘                        │  - Admin UI  │
                                       └──────────────┘

1. Server: User-Verwaltung & Auth

1.1 Datenmodell

Neue Tabelle users:

Spalte Typ Beschreibung
id UUID (PK) Eindeutige User-ID
username VARCHAR (unique) Login-Name
password_hash VARCHAR bcrypt-Hash des Passworts
created_at TIMESTAMP Erstellungszeitpunkt
is_admin BOOLEAN Admin-Flag (default: false)

Bestehende Tabelle items: um user_id (FK → users) erweitern.
Bestehende Tabelle categories/locations: um user_id (FK → users) erweitern.

1.2 Auth-Endpoints (öffentlich, kein API-Key)

Endpoint Methode Body Beschreibung
/api/auth/login POST { username, password } Gibt JWT-Token zurück
/api/auth/refresh POST { refreshToken } Neues Access-Token
  • Access-Token: kurzlebig (z.B. 1h), wird bei API-Calls als Authorization: Bearer <token> gesendet
  • Refresh-Token: langlebig (z.B. 30 Tage), zum Erneuern des Access-Tokens
  • Kein Logout-Endpoint nötig (Token läuft aus; App löscht lokal)

1.3 Admin-Endpoints (nur für is_admin = true)

Endpoint Methode Body Beschreibung
/api/admin/users GET Alle User auflisten
/api/admin/users POST { username, password } Neuen User anlegen
/api/admin/users/{id} DELETE User löschen
/api/admin/users/{id}/password PUT { password } Passwort ändern

1.4 Seed-Admin

  • Beim ersten Serverstart wird der User admin mit einem konfigurierbaren Passwort (ENV: KRISENVORRAT_ADMIN_PASSWORD) angelegt
  • Falls kein ENV gesetzt: zufälliges Passwort generieren und in die Konsole loggen

2. Server: Inventar-Endpoints (user-spezifisch)

Bisherige Endpoints werden auf den authentifizierten User geschoped:

Endpoint Methode Beschreibung
/api/inventory GET Inventar des eingeloggten Users
/api/inventory PUT Inventar des eingeloggten Users ersetzen
/api/inventory/items/{id} PATCH Einzelnen Artikel updaten (aus #56)

Die User-ID wird aus dem JWT extrahiert – kein {userId} in der URL nötig.
Der bisherige API-Key-Mechanismus wird durch JWT ersetzt.


3. Server: WebSocket für Push-Benachrichtigungen

3.1 Endpoint

/ws/sync – WebSocket, JWT-Token als Query-Parameter (?token=...) oder im ersten Frame

3.2 Nachrichten (Server → Client)

{ "type": "inventory_updated", "itemId": "abc-123", "timestamp": 1234567890 }
{ "type": "item_deleted", "itemId": "abc-123" }
{ "type": "full_sync_required" }

3.3 Verhalten

  • Server hält eine Map von userId → Set<WebSocketSession>
  • Bei jeder Inventar-Änderung (REST oder Admin) → Push an alle Sessions des betroffenen Users
  • Multi-Device: alle verbundenen Geräte eines Users erhalten Push-Events
  • Verbindungsabbruch: Client reconnected automatisch (Exponential Backoff)
  • Falls WebSocket-Verbindung nicht besteht: App holt Änderungen beim nächsten Start per GET

4. App: Login & persistente Session

4.1 Login-Screen

  • Felder: Server-URL, Username, Passwort
  • Einmaliges Login: Tokens werden im SettingsRepository gespeichert
  • Bei App-Start: Token prüfen, ggf. per Refresh-Token erneuern
  • Kein Login = Offline-Modus (lokale Daten bleiben nutzbar)

4.2 Token-Verwaltung

  • SettingsKeys: neue Keys AUTH_ACCESS_TOKEN, AUTH_REFRESH_TOKEN, AUTH_USERNAME
  • Bisheriger API_KEY wird entfernt
  • SyncService: X-API-Key Header → Authorization: Bearer <accessToken>
  • Bei 401-Response: automatisch Refresh versuchen, bei Fehler → Login-Screen

4.3 Laufende Synchronisation (App → Server)

  • Jede lokale Änderung (Insert/Update/Delete) löst sofort einen Sync-Call aus
  • Implementierung via Room-Callback oder Repository-Observer
  • Offline-Queue: Änderungen werden gepuffert und beim nächsten Online-Status gesendet

4.4 Synchronisation (Server → App)

  • Bei App-Start: GET /api/inventory → Abgleich mit lokaler DB
  • Laufend: WebSocket-Connection lauscht auf Push-Events → selektiver Sync
  • Konfliktlösung: Last-Write-Wins (Feld lastUpdated Timestamp vergleichen)

5. Server: Minimale Admin-Web-UI

5.1 Umfang

Einfache HTML-Seite (statisch, vom Ktor-Server ausgeliefert):

  • Login: Admin-Credentials eingeben
  • User-Liste: Tabelle mit Username, erstellt am, Aktionen
  • User anlegen: Formular (Username, Passwort)
  • Passwort ändern: Inline oder Modal
  • User löschen: Bestätigungsdialog

5.2 Technologie

  • Vanilla HTML + CSS + JavaScript (kein Framework)
  • Nutzt die Admin-REST-Endpoints (/api/admin/users/*)
  • Ausgeliefert unter /admin/ vom Ktor-Server (Static Content)
  • Geschützt durch Admin-JWT (gleicher Login-Flow)

6. Verschlüsselung

  • Transport: HTTPS/TLS (Reverse-Proxy oder Ktor-TLS)
  • Server-Speicherung: Klartext (bewusste Entscheidung, kein Client-seitiges Crypto)
  • Passwörter: bcrypt-gehasht in der DB
  • Tokens: JWT mit serverseitigem Secret (ENV: KRISENVORRAT_JWT_SECRET)

7. Migration & Kompatibilität

  • Bisheriger API-Key-Modus wird entfernt (Breaking Change)
  • Server: Exposed-Schema-Migration (neue users-Tabelle, user_id FK an Items/Categories/Locations)
  • App: Room-Migration (neue Token-Settings, API_KEY entfällt)
  • Bestehende Inventar-Daten am Server können einem Default-User zugewiesen oder verworfen werden

Implementierungsreihenfolge (Vorschlag)

  1. Server: User-Tabelle + Auth-Endpoints (Login, JWT)
  2. Server: Admin-Endpoints + Seed-Admin
  3. Server: Inventar-Endpoints auf User scopen
  4. App: Login-Screen + Token-Verwaltung
  5. App: SyncService auf JWT umstellen
  6. Server: WebSocket-Endpoint
  7. App: WebSocket-Client + Push-Handling
  8. Server: Admin-Web-UI
  9. App: Laufende Sync (sofortige Uploads + Offline-Queue)

Akzeptanzkriterien

  • Server: users-Tabelle mit bcrypt-Passwort-Hash
  • Server: POST /api/auth/login gibt JWT (Access + Refresh) zurück
  • Server: POST /api/auth/refresh erneuert Access-Token
  • Server: Admin-Endpoints (CRUD für User), nur für is_admin=true
  • Server: Seed-Admin beim ersten Start (konfigurierbares Passwort)
  • Server: Inventar-Endpoints liefern nur Daten des authentifizierten Users
  • Server: WebSocket /ws/sync sendet Push bei Inventar-Änderungen
  • Server: Minimale Admin-Web-UI unter /admin/
  • App: Login-Screen (Server-URL, Username, Passwort)
  • App: Persistente Session (Tokens in Settings, kein erneutes Login nötig)
  • App: Automatischer Token-Refresh bei Ablauf
  • App: Jede lokale Änderung wird sofort an den Server gesendet
  • App: Bei App-Start Server-Stand abgleichen (Last-Write-Wins)
  • App: WebSocket-Client mit Auto-Reconnect für Push-Benachrichtigungen
  • App: Offline-Queue für Änderungen ohne Netzwerk
  • Transport: HTTPS/TLS, Passwörter bcrypt-gehasht, JWT mit Server-Secret
  • Multi-Device: mehrere Geräte pro User gleichzeitig möglich
  • Bisheriger API-Key-Mechanismus entfernt
## Ziel User-Konzept einführen: Username/Passwort-basierte Authentifizierung, user-spezifische Inventare, laufende Synchronisation, WebSocket-Push-Benachrichtigungen und eine minimale Admin-Web-UI zur Benutzerverwaltung. --- ## Architektur-Überblick ``` ┌─────────────┐ HTTPS + JWT ┌──────────────┐ │ Android App │◄──────────────────────►│ Ktor Server │ │ │ WebSocket (wss) │ │ │ - Login │◄───── Push-Events ──────│ - Auth │ │ - Sync │ │ - REST API │ │ - Offline │ │ - WebSocket │ └─────────────┘ │ - Admin UI │ └──────────────┘ ``` --- ## 1. Server: User-Verwaltung & Auth ### 1.1 Datenmodell **Neue Tabelle `users`:** | Spalte | Typ | Beschreibung | |---|---|---| | `id` | UUID (PK) | Eindeutige User-ID | | `username` | VARCHAR (unique) | Login-Name | | `password_hash` | VARCHAR | bcrypt-Hash des Passworts | | `created_at` | TIMESTAMP | Erstellungszeitpunkt | | `is_admin` | BOOLEAN | Admin-Flag (default: false) | **Bestehende Tabelle `items`:** um `user_id` (FK → users) erweitern. **Bestehende Tabelle `categories`/`locations`:** um `user_id` (FK → users) erweitern. ### 1.2 Auth-Endpoints (öffentlich, kein API-Key) | Endpoint | Methode | Body | Beschreibung | |---|---|---|---| | `/api/auth/login` | POST | `{ username, password }` | Gibt JWT-Token zurück | | `/api/auth/refresh` | POST | `{ refreshToken }` | Neues Access-Token | - **Access-Token:** kurzlebig (z.B. 1h), wird bei API-Calls als `Authorization: Bearer <token>` gesendet - **Refresh-Token:** langlebig (z.B. 30 Tage), zum Erneuern des Access-Tokens - **Kein Logout-Endpoint nötig** (Token läuft aus; App löscht lokal) ### 1.3 Admin-Endpoints (nur für `is_admin = true`) | Endpoint | Methode | Body | Beschreibung | |---|---|---|---| | `/api/admin/users` | GET | – | Alle User auflisten | | `/api/admin/users` | POST | `{ username, password }` | Neuen User anlegen | | `/api/admin/users/{id}` | DELETE | – | User löschen | | `/api/admin/users/{id}/password` | PUT | `{ password }` | Passwort ändern | ### 1.4 Seed-Admin - Beim ersten Serverstart wird der User `admin` mit einem konfigurierbaren Passwort (ENV: `KRISENVORRAT_ADMIN_PASSWORD`) angelegt - Falls kein ENV gesetzt: zufälliges Passwort generieren und in die Konsole loggen --- ## 2. Server: Inventar-Endpoints (user-spezifisch) Bisherige Endpoints werden auf den authentifizierten User geschoped: | Endpoint | Methode | Beschreibung | |---|---|---| | `/api/inventory` | GET | Inventar **des eingeloggten Users** | | `/api/inventory` | PUT | Inventar **des eingeloggten Users** ersetzen | | `/api/inventory/items/{id}` | PATCH | Einzelnen Artikel updaten (aus #56) | Die User-ID wird aus dem JWT extrahiert – kein `{userId}` in der URL nötig. Der bisherige API-Key-Mechanismus wird durch JWT ersetzt. --- ## 3. Server: WebSocket für Push-Benachrichtigungen ### 3.1 Endpoint `/ws/sync` – WebSocket, JWT-Token als Query-Parameter (`?token=...`) oder im ersten Frame ### 3.2 Nachrichten (Server → Client) ```json { "type": "inventory_updated", "itemId": "abc-123", "timestamp": 1234567890 } { "type": "item_deleted", "itemId": "abc-123" } { "type": "full_sync_required" } ``` ### 3.3 Verhalten - Server hält eine Map von `userId → Set<WebSocketSession>` - Bei jeder Inventar-Änderung (REST oder Admin) → Push an alle Sessions des betroffenen Users - Multi-Device: alle verbundenen Geräte eines Users erhalten Push-Events - Verbindungsabbruch: Client reconnected automatisch (Exponential Backoff) - Falls WebSocket-Verbindung nicht besteht: App holt Änderungen beim nächsten Start per GET --- ## 4. App: Login & persistente Session ### 4.1 Login-Screen - Felder: Server-URL, Username, Passwort - Einmaliges Login: Tokens werden im SettingsRepository gespeichert - Bei App-Start: Token prüfen, ggf. per Refresh-Token erneuern - Kein Login = Offline-Modus (lokale Daten bleiben nutzbar) ### 4.2 Token-Verwaltung - `SettingsKeys`: neue Keys `AUTH_ACCESS_TOKEN`, `AUTH_REFRESH_TOKEN`, `AUTH_USERNAME` - Bisheriger `API_KEY` wird entfernt - SyncService: `X-API-Key` Header → `Authorization: Bearer <accessToken>` - Bei 401-Response: automatisch Refresh versuchen, bei Fehler → Login-Screen ### 4.3 Laufende Synchronisation (App → Server) - Jede lokale Änderung (Insert/Update/Delete) löst sofort einen Sync-Call aus - Implementierung via Room-Callback oder Repository-Observer - Offline-Queue: Änderungen werden gepuffert und beim nächsten Online-Status gesendet ### 4.4 Synchronisation (Server → App) - **Bei App-Start:** `GET /api/inventory` → Abgleich mit lokaler DB - **Laufend:** WebSocket-Connection lauscht auf Push-Events → selektiver Sync - **Konfliktlösung:** Last-Write-Wins (Feld `lastUpdated` Timestamp vergleichen) --- ## 5. Server: Minimale Admin-Web-UI ### 5.1 Umfang Einfache HTML-Seite (statisch, vom Ktor-Server ausgeliefert): - **Login:** Admin-Credentials eingeben - **User-Liste:** Tabelle mit Username, erstellt am, Aktionen - **User anlegen:** Formular (Username, Passwort) - **Passwort ändern:** Inline oder Modal - **User löschen:** Bestätigungsdialog ### 5.2 Technologie - Vanilla HTML + CSS + JavaScript (kein Framework) - Nutzt die Admin-REST-Endpoints (`/api/admin/users/*`) - Ausgeliefert unter `/admin/` vom Ktor-Server (Static Content) - Geschützt durch Admin-JWT (gleicher Login-Flow) --- ## 6. Verschlüsselung - **Transport:** HTTPS/TLS (Reverse-Proxy oder Ktor-TLS) - **Server-Speicherung:** Klartext (bewusste Entscheidung, kein Client-seitiges Crypto) - **Passwörter:** bcrypt-gehasht in der DB - **Tokens:** JWT mit serverseitigem Secret (ENV: `KRISENVORRAT_JWT_SECRET`) --- ## 7. Migration & Kompatibilität - Bisheriger API-Key-Modus wird entfernt (Breaking Change) - Server: Exposed-Schema-Migration (neue `users`-Tabelle, `user_id` FK an Items/Categories/Locations) - App: Room-Migration (neue Token-Settings, API_KEY entfällt) - Bestehende Inventar-Daten am Server können einem Default-User zugewiesen oder verworfen werden --- ## Implementierungsreihenfolge (Vorschlag) 1. **Server: User-Tabelle + Auth-Endpoints** (Login, JWT) 2. **Server: Admin-Endpoints + Seed-Admin** 3. **Server: Inventar-Endpoints auf User scopen** 4. **App: Login-Screen + Token-Verwaltung** 5. **App: SyncService auf JWT umstellen** 6. **Server: WebSocket-Endpoint** 7. **App: WebSocket-Client + Push-Handling** 8. **Server: Admin-Web-UI** 9. **App: Laufende Sync (sofortige Uploads + Offline-Queue)** --- ## Akzeptanzkriterien - [ ] Server: `users`-Tabelle mit bcrypt-Passwort-Hash - [ ] Server: `POST /api/auth/login` gibt JWT (Access + Refresh) zurück - [ ] Server: `POST /api/auth/refresh` erneuert Access-Token - [ ] Server: Admin-Endpoints (CRUD für User), nur für `is_admin=true` - [ ] Server: Seed-Admin beim ersten Start (konfigurierbares Passwort) - [ ] Server: Inventar-Endpoints liefern nur Daten des authentifizierten Users - [ ] Server: WebSocket `/ws/sync` sendet Push bei Inventar-Änderungen - [ ] Server: Minimale Admin-Web-UI unter `/admin/` - [ ] App: Login-Screen (Server-URL, Username, Passwort) - [ ] App: Persistente Session (Tokens in Settings, kein erneutes Login nötig) - [ ] App: Automatischer Token-Refresh bei Ablauf - [ ] App: Jede lokale Änderung wird sofort an den Server gesendet - [ ] App: Bei App-Start Server-Stand abgleichen (Last-Write-Wins) - [ ] App: WebSocket-Client mit Auto-Reconnect für Push-Benachrichtigungen - [ ] App: Offline-Queue für Änderungen ohne Netzwerk - [ ] Transport: HTTPS/TLS, Passwörter bcrypt-gehasht, JWT mit Server-Secret - [ ] Multi-Device: mehrere Geräte pro User gleichzeitig möglich - [ ] Bisheriger API-Key-Mechanismus entfernt
Sign in to join this conversation.
No description provided.