Files
harheimertc/ANDROID_KOTLIN_PLAN.md
Torsten Schulz (local) 58fd7fa5c6
Some checks failed
Code Analysis and Production Deploy / analyze (push) Failing after 5m7s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Has been skipped
feat(auth): implement Android refresh token handling and session management
- Added support for generating Android access tokens and managing refresh sessions in the auth endpoints.
- Implemented new tests for login, logout, and refresh functionalities specific to Android clients.
- Enhanced password reset logging with normalization and masking of email addresses.
- Created a new diagnostics endpoint for password reset attempts, including filtering and summarizing logs.
- Introduced a new utility for managing password reset logs with retention policies.
- Added tests for password reset log utilities to ensure proper functionality and privacy compliance.
- Updated WebAuthn configuration tests to validate origin handling for production and allowed origins.
2026-05-27 19:34:53 +02:00

17 KiB

Android App — Kotlin (Jetpack Compose) Plan und Abhakliste

Kurz: Ziel ist eine native Android-App mit Kotlin + Jetpack Compose, die die Web-UI 1:1 abbildet (Farben, Typografie, Funktionalitäten). Diese Datei enthält Architekturentscheidungen, empfohlene Bibliotheken und eine detaillierte Abhakliste (schrittweise).

  1. Zusammenfassung der Entscheidungen
  • Plattform: Native Android
  • Sprache: Kotlin
  • UI-Toolkit: Jetpack Compose (Compose Material3)
  • Architektur: MVVM mit ViewModel + Kotlin Coroutines + Flow
  • DI: Hilt
  • HTTP-Client: Ktor Client oder Retrofit + OkHttp (empfohlen: Retrofit für breite Community-Docs)
  • Bild-Loading: Coil
  • Lokale DB / Caching: Room + DataStore (Preferences)
  • Background/Sync: WorkManager
  • Auth: kurzlebiges JWT-Access-Token plus rotierendes, widerrufbares Refresh-Token pro Android-Gerätesitzung; Speicherung in EncryptedSharedPreferences/Android Keystore; Unterstützung für Passkeys (Android Passkeys / WebAuthn Interop über FIDO2 APIs)
  • Auth-Sicherheitsentscheidung: kein statischer App-Key bzw. kein in der APK hinterlegtes Client-Secret. Native Apps können ein gemeinsames Secret nicht vertraulich halten. Optional später: Refresh-Sitzung an ein pro Installation im Android Keystore erzeugtes Schlüsselpaar binden.
  • Rich-Text: WebView-basierte Anzeige; Editoren: ggf. hybride Lösung (Server-side HTML editor + WebView) oder RichEditor-Libs
  • Crash-Reporting & Monitoring: Firebase Crashlytics oder Sentry
  1. Design & Farben
  • Material Theme (Material3) mit Farben aus tailwind.config.js (Primary + Accent).
  • Fonts: Inter & Montserrat via Google Fonts (Download/Bundle oder Play-Services-Download at runtime).
  • Mapping: Tailwind-Token → colors.xml / Compose Color tokens.
  1. Empfohlene Abhängigkeiten (erste Implementierung)
  • androidx.compose.* (ui, material3, navigation)
  • androidx.lifecycle:lifecycle-viewmodel-ktx
  • com.google.dagger:hilt-android
  • retrofit2 + converter-moshi / kotlinx-serialization
  • io.coil-kt:coil-compose
  • androidx.room:room-runtime + room-ktx
  • androidx.work:work-runtime-ktx
  • androidx.datastore:datastore-preferences
  • com.google.android.gms:play-services-auth (für passkeys falls nötig)
  • io.sentry:sentry-android (optional)
  1. Detaillierte Abhakliste (Schritte) [x] 1. Repo-Analyse: Liste der externen Endpunkte und Auth-Anforderungen exportieren [x] 2. Projekt-Scaffold: Android Studio Projekt mit Kotlin + Compose anlegen [x] 3. App-Architektur: Module / Packages anlegen (ui, data, domain, di, util) [x] 4. CI-Build: Gradle-Config und GitHub Actions Skeleton [x] 5. Theme: Color.kt, Typography.kt, Theme.kt erstellen und Tailwind-Farben mappen [x] 6. Fonts: Inter + Montserrat einbinden (res/font oder GoogleFonts) [ ] 7. Navigation: Compose Navigation-Graph mit Routen für alle Web-Seiten anlegen [x] 7a. Umgebungen: Android-Varianten fuer lokal, Test-Instanz und Produktion mit eigener API-Basis konfigurieren [x] 7b. Adaptive Navigation: Tablet mit persistentem Header/Hauptmenue, Smartphone mit bestehender screenbezogener Navigation [x] 7c. Branding: vorhandenes Web-Logo als optimierte Android-Ressource in der App-Navigation verwenden [x] 7d. Tablet-Navigation: öffentliche Haupt- und Subnavigation der Web-UI mit Portierungszielen abbilden [x] 7e. Navigation: dynamische Mannschaftslinks, Galerie-Sichtbarkeit und rollenabhängiges Intern wie in der Web-UI anbinden [x] 8. Start-Screen: HomeScreen webnah mit Hero, Navigation, Termine, Spielen, News und Aktionen umsetzen [x] 9. Komponenten: NavBar, Footer, Cards, ImageGrid und News-Dialog implementieren [x] 10. Öffentliche Screens aus der Web-Navigation portieren

    • / Startseite
    • /termine: öffentliche Terminliste mit Lade-, Leer- und Fehlerzustand
    • /mannschaften/spielplaene: Saison-, Wettbewerbs- und Mannschaftsfilter mit Spielkarten
    • /verein/galerie: Anzeige-Screen vorhanden
    • /kontakt: Formular-Screen vorhanden
    • /mitgliedschaft: Antrag, Validierung, PDF-Erzeugung und PDF-Öffnen
    • /verein/ueber-uns: CMS-Inhalt aus der öffentlichen Konfiguration
    • /vorstand: öffentliche Vorstandsangaben aus der Konfiguration
    • /verein/geschichte: CMS-Inhalt aus der öffentlichen Konfiguration
    • /verein/satzung: CMS-Inhalt und PDF-Aufruf aus der öffentlichen Konfiguration
    • /vereinsmeisterschaften: Ergebnisliste mit Jahresfilter und Statistik
    • /links: strukturierte CMS-Links mit Fallback-Verweisen
    • /mannschaften: Übersicht aus saisonaler Mannschafts-CSV
    • /mannschaften/[slug]: dynamische Mannschaftsdetails mit aktuellem Spielplan und Umschaltung Matches/Tabelle
    • /spielsysteme: Spielsystemkarten mit Kategoriefilter aus CSV
    • /training: Trainingsort und gruppierte Trainingszeiten aus der Konfiguration
    • /training/trainer
    • /training/anfaenger
    • /tt-regeln: Regelübersicht mit DTTB- und PDF-Aufruf [ ] 10a. Weitere öffentliche bzw. bestehende Web-Routen prüfen und portieren
    • /anlagen
    • /impressum
    • Legacy-/Doppelrouten klären: /galerie, /geschichte, /satzung, /ueber-uns, /spielplan, /verein/tt-regeln, /mannschaft/[slug], /mannschaften/herren, /mannschaften/damen, /mannschaften/jugend [ ] 10b. Newsletter-Screens portieren
    • /newsletter/subscribe
    • /newsletter/unsubscribe
    • /newsletter/confirm, /newsletter/confirmed, /newsletter/unsubscribed [x] 10c. Auth-Screens portieren
    • /login: Passwort-Login und Logout in der laufenden Sitzung
    • /registrieren
    • /passwort-vergessen [ ] 10d. Mitgliederbereich portieren
    • /mitgliederbereich: Übersicht
    • /mitgliederbereich/mitglieder
    • /mitgliederbereich/news
    • /mitgliederbereich/profil
    • /mitgliederbereich/api [ ] 10e. CMS-Screens nach Rollenberechtigung portieren
    • /cms, /cms/startseite, /cms/inhalte, /cms/vereinsmeisterschaften
    • /cms/sportbetrieb, /cms/mitgliederverwaltung, /cms/kontaktanfragen
    • /cms/newsletter, /cms/einstellungen, /cms/benutzer [ ] 11. API-Client: Retrofit/Ktor-Client implementieren, Auth-Interceptor (Token Refresh)
    • Retrofit/OkHttp/Moshi und Hilt-Verdrahtung
    • Öffentliche Endpunkte für Startseite, Termine, Spielplan, Galerie und Kontakt
    • Mitgliedschafts-PDF-Endpunkte mit Cookie-Jar und FileProvider
    • Passwort-Login-Endpunkt und Token-Übergabe an den Interceptor
    • Verschlüsselte Token-Persistenz sowie Status/Logout per Bearer-Token
    • Bearer-Unterstützung aller später portierten geschützten Bereiche
    • POST /api/auth/refresh anbinden und Access-Token bei Ablauf automatisch erneuern
    • OkHttp-Authenticator mit genau einem synchronisierten Refresh-Versuch pro fehlgeschlagenem Request ergänzen [x] 12. Auth: Login/Register/Logout + sichere Token-Speicherung (EncryptedSharedPreferences)
    • Login/Logout und verschlüsselte Token-Speicherung
    • Registrierung und Passwort-Reset
    • Backend: Android-JWT-Access-Token auf ca. 15 Minuten reduzieren; bestehende Web-Cookie-Sitzungen bis zur Web-Refresh-Integration kompatibel weiterführen
    • Backend: langlebige, zufällige Refresh-Token pro Gerätesitzung mit serverseitig gespeichertem Token-Hash einführen
    • Backend: Refresh-Token bei jeder Erneuerung rotieren und Wiederverwendung eines verbrauchten Tokens als Sitzungsdiebstahl behandeln
    • Backend: Logout, Kontodeaktivierung und Passwortänderung widerrufen betroffene Refresh-Sitzungen
    • App: Access- und Refresh-Token verschlüsselt speichern, Sitzung beim App-Start durch Refresh wiederherstellen
    • Optional nach MVP: pro Installation ein nicht exportierbares Keystore-Schlüsselpaar erzeugen und Refresh-Requests daran binden [ ] 13. Passkeys: Integration prüfen (FIDO2 / Passkeys) und Fallback auf Passwort [ ] 14. Image-Upload: Multipart-Upload + Coil für Anzeige + Bildkompression (u. a. Sharp-Äquivalent evtl. serverseitig) [ ] 15. Rich-Text: Anzeige von HTML (Compose + WebView) und ggf. Editor via WebView-bridge [ ] 16. Formulare: Validierung (clientseitig) und Fehlerdarstellung [ ] 17. Offline & Caching: Room für persistente Daten, Response-Caching, Sync-Strategie [ ] 18. Lokalisierung: strings.xml (DE + EN) und i18n-Check [ ] 19. Accessibility: ContentDescription, Focus, Farben/Kontrast prüfen [ ] 20. Tests: Unit-Tests für ViewModels + UI-Tests mit Compose Testing [ ] 21. Performance: Bildoptimierung, LazyLists, Paging (falls große Daten) [ ] 22. Analytics: Firebase / Matomo Integration (je nach Datenschutz) [ ] 23. Crash-Reporting: Sentry / Crashlytics integrieren [ ] 24. Build/Release: App signing, Release-Notes, Play-Store-Metadaten vorbereiten [ ] 25. Dokumentation: README-android.md mit Setup, Architektur und Release-Anleitung
  2. Kurzzeit-MVP (Priorität für erste Version)

  • A. Auth (Login/Logout)
    • Passwort-Login und Logout in der aktuellen App-Sitzung
    • Persistente Statuswiederherstellung/Logout für die Auth-Endpunkte
    • Dauerhaftes Eingeloggtbleiben durch rotierendes Refresh-Token pro Android-Gerätesitzung
  • B. Home, Termine, Spielplan, Galerie (anzeigen)
  • C. Kontaktformular (absenden)
  • D. Bildanzeige + Caching
  • E. Theme & Fonts
  1. Nächste Aktionen (sofort)
  • Web-Login bei Bedarf auf denselben Refresh-Flow migrieren; bis dahin bleiben Web-Cookie-Sitzungen bewusst kompatibel.
  • Passkey-Anmeldung über Android Credential Manager anbinden.
  • Die noch fehlenden öffentlichen Routen aus 10a und die Newsletter-Screens aus 10b nativ portieren.
  • Saisonwahl für Mannschaftsübersicht/-details wie in der Web-UI ergänzen.
  • Mitgliederbereich/CMS-Screens portieren; dabei rollenabhängige Intern-Navigation und Bearer-Unterstützung der verwendeten Backend-Endpunkte ergänzen.
  1. Umsetzungsprotokoll
  • 2026-05-27: Webnahe Startseite mit öffentlichen Live-Daten umgesetzt; Hilt/Moshi-App-Verdrahtung ergänzt.
  • 2026-05-27: Termine und Spielplan als native Screens umgesetzt; Spielplan unterstützt Saison, Wettbewerb, Mannschaft, Ergebnis und zweizeilige Gruppeninformation.
  • 2026-05-27: Mitgliedschaft mit Antrag/PDF-Abruf sowie Passwort-Login/Logout umgesetzt; offene Auth-Härtung separat ausgewiesen.
  • 2026-05-27: Tokens verschlüsselt persistiert; Session-Wiederherstellung sowie Logout per Bearer-Token in den Auth-Endpunkten ermöglicht.
  • 2026-05-27: Registrierung und Passwort-Reset an die vorhandenen Auth-Endpunkte angebunden.
  • 2026-05-27: Product-Flavors local, instantTest und production eingerichtet; lokale Basis-URL ist per Gradle-Parameter überschreibbar.
  • 2026-05-27: Gradle-Heap/Worker für Flavor-Builds festgelegt, nachdem paralleles D8/KSP mit dem 512-MiB-Standardheap nicht ausreichend Speicher hatte.
  • 2026-05-27: Lokales Testsetup gegen Emulator geprüft; bei IPv6-gebundenem Nuxt-Dev-Server wird die von Nuxt ausgegebene Network-URL per LOCAL_API_BASE_URL verwendet.
  • 2026-05-27: Adaptive Navigation umgesetzt; Tablet-Layouts ab 600dp zeigen Header und Hauptmenue dauerhaft, Smartphone-Layouts behalten die vorhandene Navigation.
  • 2026-05-27: Platzhalterlogo in der Android-Navigation durch das vorhandene Harheimer-TC-Weblogo als skalierte lokale PNG-Ressource ersetzt.
  • 2026-05-27: Web-Navigation und pages/ vollständig inventarisiert; Tablet-Haupt-/Subnavigation für die öffentlichen Bereiche strukturell angeglichen und alle fehlenden Screens einzeln in die Portierungsliste aufgenommen.
  • 2026-05-27: Tablet-Header auf Web-Verhalten angepasst (Bereichswechsel öffnet Startseite und Submenü) und die native ActionBar zugunsten des App-Headers entfernt.
  • 2026-05-27: Navigation mit Live-Mannschaftslinks, öffentlicher Galerie-Sichtbarkeit und rollenabhängigem Intern ergänzt; Mannschaftsübersicht/-detail sowie Training, Trainer und Anfänger nativ portiert.
  • 2026-05-27: Mannschaftsdetail um die Web-Untertabs Matches und Tabelle erweitert; Tabellenzeilen werden aus /api/spielplan/table geladen und die eigene Mannschaft hervorgehoben.
  • 2026-05-27: Tabellenraster in den Mannschaftsdetails mit gemeinsamen Spaltenbreiten für Tablet und Smartphone ausgerichtet; die Zustandswiederherstellung dynamischer Mannschaftslinks korrigiert.
  • 2026-05-27: Die verbleibenden öffentlichen Screens aus Punkt 10 portiert: Verein/CMS-Inhalte, Vorstand, Satzung/PDF, Links, Vereinsmeisterschaften mit Personenbild-Dialog, Spielsysteme und TT-Regeln.
  • 2026-05-27: Architektur für dauerhaftes Android-Login festgelegt: kein eingebetteter App-Key, sondern kurzlebige Access-Tokens und rotierende, widerrufbare Refresh-Tokens pro Gerätesitzung; optionale spätere Gerätebindung per Keystore-Schlüsselpaar.
  • 2026-05-27: Dauerhaftes Android-Login umgesetzt: Android-Logins erhalten 15-Minuten-Access-Tokens und rotierende Refresh-Tokens; Token-Hashes, Wiederverwendungswiderruf, Logout-/Reset-/Deaktivierungswiderruf sowie verschlüsselte App-Speicherung und automatischer OkHttp-Refresh sind implementiert.
  1. Android-Testumgebungen
  • Lokal im Emulator: ./gradlew :app:installLocalDebug verwendet http://10.0.2.2:3100/ und die App-ID de.harheimertc.local.
  • Lokal, wenn 10.0.2.2 nicht erreichbar ist: ./gradlew :app:installLocalDebug -PLOCAL_API_BASE_URL=http://<NUXT-NETWORK-HOST>:3100/; die passende URL steht in der npm run dev-Ausgabe (hier http://torstens:3100/).
  • Test-Instanz: ./gradlew :app:installInstantTestDebug verwendet https://harheimertc.tsschulz.de/ und die App-ID de.harheimertc.test.
  • Produktion: ./gradlew :app:installProductionDebug verwendet https://harheimertc.de/ und die App-ID de.harheimertc.
  • Nur APKs erzeugen: ./gradlew :app:assembleLocalDebug :app:assembleInstantTestDebug :app:assembleProductionDebug.
  1. Dauerhaftes Android-Login: Architektur und Umsetzung
  • Stand der Umsetzung: Android-Logins erhalten ein ca. 15 Minuten gültiges JWT und eine serverseitig prüfbare Refresh-Sitzung; Access-Tokens mit Sitzungs-ID werden bei widerrufener Gerätesitzung abgelehnt. Web-Logins verwenden weiterhin das bisherige Cookie-JWT, bis ein browserseitiger Refresh-Flow ergänzt ist.
  • Ziel: Ein Benutzer bleibt auf einem bekannten Gerät angemeldet, ohne dass ein langfristig gültiges Bearer-JWT oder ein extrahierbares App-Secret verwendet wird.
  • Token-Modell:
    • Access-Token: JWT mit kurzer Laufzeit, Zielwert ca. 15 Minuten; wird für normale API-Requests als Bearer-Token verwendet.
    • Refresh-Token: kryptografisch zufälliges, undurchsichtiges Token mit längerer Laufzeit, Zielwert z. B. 90 Tage mit Erneuerung bei aktiver Nutzung.
    • Server speichert ausschließlich den Hash des Refresh-Tokens zusammen mit sessionId, userId, createdAt, lastUsedAt, expiresAt, revokedAt und optional Gerätebezeichnung.
  • Backend-Arbeitspaket:
    • Login-Antwort um accessToken, refreshToken, sessionId und Ablaufmetadaten erweitern; bestehendes token nur befristet kompatibel halten.
    • POST /api/auth/refresh implementieren: gültiges Refresh-Token konsumieren, rotieren und ein neues Token-Paar zurückgeben.
    • Token-Wiederverwendung erkennen: Wird ein rotiertes Refresh-Token erneut präsentiert, die betroffene Token-Familie bzw. Gerätesitzung widerrufen.
    • POST /api/auth/logout auf Widerruf der Gerätesitzung erweitern; optional Endpunkte zum Anzeigen und Widerrufen eigener Geräte-Sitzungen vorsehen.
    • Kontodeaktivierung und Passwortänderung müssen sämtliche Refresh-Sitzungen des Benutzers widerrufen.
    • Rate-Limits und Audit-Events für Login, Refresh-Erfolg/-Fehlschlag, Wiederverwendung und Widerruf ergänzen.
  • Android-Arbeitspaket:
    • AuthRepository auf Access-Token, Refresh-Token und Session-ID erweitern; Speicherung weiter über Keystore-geschützte Preferences.
    • ApiService/DTOs um Refresh-Request und Token-Paar-Antwort ergänzen.
    • Einen OkHttp-Authenticator einsetzen, der auf 401 einmalig ein Access-Token erneuert, parallele Refreshes synchronisiert und den ursprünglichen Request wiederholt.
    • Beim App-Start zunächst Access-Token prüfen und bei Ablauf transparent mit dem Refresh-Token erneuern; nur bei fehlgeschlagenem Refresh zum Login zurückkehren.
    • Beim Logout lokale Tokens auch bei Netzwerkfehler entfernen; serverseitiger Widerruf erfolgt best effort bzw. bei nächster Konnektivität.
  • Sicherheitsregeln:
    • Kein gemeinsamer App-Key und kein statisches Client-Secret in Sourcecode, BuildConfig oder APK.
    • Refresh-Tokens nie im Klartext serverseitig speichern oder protokollieren.
    • Nur HTTPS für Test-/Produktionsumgebungen; Token-Werte nicht in Logging-Interceptors ausgeben.
    • Optional nach MVP: App erzeugt pro Installation ein Keystore-Schlüsselpaar; Backend bindet Refresh-Sitzungen an den öffentlichen Schlüssel und prüft signierte Refresh-Anfragen.

Datei: ANDROID_KOTLIN_PLAN.md