feat(auth): Login-Timeout (10s) mit Hinweisbanner
Haengt der interaktive Login (Browser-Tab/Token-Exchange) bei Verbindungsabbruch/Issuer-Hang, bricht er nach 10s ab; LoginPage zeigt 'Einloggen nicht moeglich. Spaeter erneut versuchen.' (Unauthenticated.loginTimedOut). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -48,15 +48,32 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
|
||||
late final StreamSubscription<AuthSessionEvent> _subscription;
|
||||
|
||||
/// Timeout für den interaktiven Login. Schützt den Fahrer vor dem
|
||||
/// „Spinner dreht ewig"-Fall, wenn der native `flutter_appauth`-Aufruf
|
||||
/// (Browser-Tab + Token-Exchange) im Verbindungsabbruch / Issuer-Hang
|
||||
/// stecken bleibt. Bewusst etwas knapper als der Restore-Timeout
|
||||
/// (15 s im `_handleRestore`) — beim manuellen Login wartet der User
|
||||
/// aktiv vor dem Bildschirm.
|
||||
static const Duration _loginTimeout = Duration(seconds: 10);
|
||||
|
||||
Future<void> _handleLogin(
|
||||
LoginRequested event,
|
||||
Emitter<AuthState> emit,
|
||||
) async {
|
||||
try {
|
||||
emit(Authenticating());
|
||||
await tokenProvider.login();
|
||||
await tokenProvider.login().timeout(_loginTimeout);
|
||||
// Erfolg landet via Stream-Subscription als
|
||||
// ProviderSessionChanged.loggedIn → _handleProviderEvent.
|
||||
} on TimeoutException {
|
||||
// Native Login-Routine läuft im Hintergrund ggf. weiter. Sollte sie
|
||||
// doch noch erfolgreich durchlaufen, feuert der Provider-Stream
|
||||
// später ein `AuthLoggedIn` — der ProviderEvent-Handler hebt den
|
||||
// State dann auf `Authenticated`. Kein Schaden, nur „nachträgliche
|
||||
// Anmeldung". Bei späterer Exception passiert nichts; das Future
|
||||
// ist hier nicht mehr awaited.
|
||||
debugPrint('Login-Timeout nach ${_loginTimeout.inSeconds}s.');
|
||||
emit(Unauthenticated(loginTimedOut: true));
|
||||
} catch (err, st) {
|
||||
debugPrint('Login fehlgeschlagen: $err\n$st');
|
||||
emit(Unauthenticated());
|
||||
|
||||
@ -10,7 +10,14 @@ class AuthBootstrapping extends AuthState {}
|
||||
|
||||
class Unauthenticated extends AuthState {
|
||||
final bool sessionExpired;
|
||||
Unauthenticated({this.sessionExpired = false});
|
||||
|
||||
/// `true`, wenn der letzte Login-Versuch in das 10-s-Timeout im
|
||||
/// [AuthBloc] gelaufen ist (z. B. Verbindungsabbruch während
|
||||
/// `tokenProvider.login()` oder hängender Issuer). Die [LoginPage]
|
||||
/// blendet daraufhin einen Hinweis ein.
|
||||
final bool loginTimedOut;
|
||||
|
||||
Unauthenticated({this.sessionExpired = false, this.loginTimedOut = false});
|
||||
}
|
||||
|
||||
/// Transient state während dem PKCE-Flow (Browser-Tab offen,
|
||||
|
||||
Reference in New Issue
Block a user