Files
Holzleitner-Lieferservice-App/lib/feature/authentication/bloc/auth_event.dart
Dennis Nemec 6d7e58fc0f Phase B: Keycloak OIDC (PKCE) statt Cookie-Session-Login
App-Code:
- KeycloakOidcTokenProvider: PKCE-Login via flutter_appauth, Refresh via
  Refresh-Token aus flutter_secure_storage, Session-Restore beim
  App-Start, Logout.
- AuthSessionEvent als Provider→Bloc-Brücke (LoggedIn/LoggedOut/
  SessionExpired) auf einem Broadcast-Stream.
- AuthBloc komplett umgebaut: nimmt jetzt den KeycloakOidcTokenProvider
  statt UserInfoService, mappt eingehende Provider-Events auf eigene
  Zustände. Authenticated.fromClaims() liest personalnummer + Name aus
  dem ID-Token-Payload.
- LoginPage: kein Browser+Deep-Link mehr — Button feuert
  LoginRequested, der Provider übernimmt den restlichen Flow.
- network_locator: produktiver KeycloakOidcTokenProvider, doppelt
  registriert (KeycloakOidcTokenProvider für AuthBloc,
  AuthTokenProvider für Interceptor).
- Auth-State trägt zusätzlich personalnummer/displayName/email; das
  Legacy-User-Objekt + sessionId bleiben temporär drin, damit die
  alten ERPframe-Services (Phase D) noch kompilieren.

Plattform-Setup:
- Android: appAuthRedirectScheme=holzleitner in build.gradle.kts,
  NetworkSecurityConfig erlaubt HTTP zu localhost/10.0.2.2/127.0.0.1.
- iOS: holzleitner als URL-Scheme im Info.plist, ATS-Ausnahme für
  localhost (HTTP-Keycloak im Dev-Setup).

Out of scope:
- Keine echte App-Run-Smoke — kommt mit dem User-Test.
- iOS-pod-install läuft beim ersten 'flutter run ios' automatisch.
- Old ERPframe-Services bleiben aktiv und werfen ab jetzt 401 (kein
  Cookie-Session-Token mehr) — wird in Phase D entfernt.
2026-05-14 22:59:36 +02:00

49 lines
1.6 KiB
Dart

/// Events des AuthBloc.
///
/// Eingehende Events (Trigger): `LoginRequested`, `LogoutRequested`,
/// `RestoreSessionRequested`.
///
/// Stream-Events vom `KeycloakOidcTokenProvider` werden über
/// `ProviderSessionChanged` in den Bloc-Bus übersetzt — damit alles
/// in der `on<...>`-Maschinerie des Blocs verarbeitet werden kann.
sealed class AuthEvent {
const AuthEvent();
}
/// Vom UI ausgelöst — startet den PKCE-Login-Flow.
final class LoginRequested extends AuthEvent {
const LoginRequested();
}
/// Vom UI ausgelöst — beendet die Session.
final class LogoutRequested extends AuthEvent {
const LogoutRequested();
}
/// Vom App-Bootstrap ausgelöst — prüft, ob es einen persistierten
/// Refresh-Token gibt, und stellt die Session ggf. wieder her.
final class RestoreSessionRequested extends AuthEvent {
const RestoreSessionRequested();
}
/// Interner Event-Typ — Brücke vom Token-Provider-Stream in die
/// Bloc-Maschine.
final class ProviderSessionChanged extends AuthEvent {
const ProviderSessionChanged(this.kind, {this.claims, this.accessToken});
final ProviderEventKind kind;
final Map<String, dynamic>? claims;
final String? accessToken;
}
enum ProviderEventKind { loggedIn, loggedOut, sessionExpired }
/// Legacy-Event: Wird von den alten ERPframe-Repositories gefeuert,
/// wenn der Server mit 401 antwortet. Mit Phase D fliegt das raus,
/// weil 401 dann direkt vom `HolzleitnerAuthInterceptor` ausgewertet
/// und an den Provider gemeldet wird.
final class SessionExpiredEvent extends AuthEvent {
const SessionExpiredEvent();
}