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.
27 lines
870 B
Dart
27 lines
870 B
Dart
/// Events, die der `KeycloakOidcTokenProvider` über seinen
|
|
/// Broadcast-Stream auswirft. Der AuthBloc abonniert diesen Stream
|
|
/// und reagiert mit eigenen Zustands-Übergängen.
|
|
///
|
|
/// Bewusst eigene Events (statt direkter Bloc-Aufrufe), damit der
|
|
/// Token-Provider keine Abhängigkeit auf die Bloc-Schicht braucht.
|
|
sealed class AuthSessionEvent {
|
|
const AuthSessionEvent();
|
|
}
|
|
|
|
/// Erfolgreicher Login (frisch oder restauriert).
|
|
final class AuthLoggedIn extends AuthSessionEvent {
|
|
const AuthLoggedIn(this.claims);
|
|
final Map<String, dynamic> claims;
|
|
}
|
|
|
|
/// Sauberer Logout durch den Nutzer.
|
|
final class AuthLoggedOut extends AuthSessionEvent {
|
|
const AuthLoggedOut();
|
|
}
|
|
|
|
/// Refresh fehlgeschlagen oder Server lehnt Token ab — die App muss
|
|
/// zurück zur Login-Page.
|
|
final class AuthSessionExpired extends AuthSessionEvent {
|
|
const AuthSessionExpired();
|
|
}
|