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.
49 lines
1.6 KiB
Dart
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();
|
|
}
|
|
|