From 49d97bbdb27ef170d59d17dce4e896628f544715 Mon Sep 17 00:00:00 2001 From: Md Habibur Rahman <42869623+aniklavida@users.noreply.github.com> Date: Thu, 1 Oct 2026 01:07:20 +0600 Subject: [PATCH] Ignore a silent refresh that finishes after the user signed out A periodic token refresh already in flight when the user signed out could complete afterwards and sign them back in, so the login screen never appeared. The session now records when it was cleared and discards results of refreshes that started before that. Agent: Claude Co-authored-by: AnikLaVida Bot <328546428+aniklavida-bot@users.noreply.github.com> Co-Authored-By: Claude Sonnet 5.5 --- .../flutter/lib/shared/auth/session_controller.dart | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mobile/flutter/lib/shared/auth/session_controller.dart b/mobile/flutter/lib/shared/auth/session_controller.dart index 791a5c6..e5297e9 100644 --- a/mobile/flutter/lib/shared/auth/session_controller.dart +++ b/mobile/flutter/lib/shared/auth/session_controller.dart @@ -31,6 +31,9 @@ class SessionController extends ChangeNotifier implements SessionPort { final TokenStore _tokenStore; Timer? _refreshTimer; bool _bootstrapped = false; + // Bumped whenever the session is cleared so a refresh that was already in + // flight cannot sign the user back in after they signed out. + int _generation = 0; AuthStatus status = AuthStatus.loading; User? user; @@ -72,11 +75,12 @@ class SessionController extends ChangeNotifier implements SessionPort { @override Future adoptSession(TokenPair tokens) => _adopt(tokens); - Future _adopt(TokenPair tokens) async { + Future _adopt(TokenPair tokens, {int? generation}) async { final userResponse = await _apiClient.authApi.getCurrentUser( headers: {'Authorization': 'Bearer ${tokens.accessToken}'}, ); final resolvedUser = userResponse.data!; + if (generation != null && generation != _generation) return resolvedUser; _accessToken = tokens.accessToken; _expiresAt = tokens.expiresAt; @@ -91,6 +95,7 @@ class SessionController extends ChangeNotifier implements SessionPort { @override Future clearSession() async { + _generation++; _refreshTimer?.cancel(); _refreshTimer = null; _accessToken = null; @@ -118,12 +123,15 @@ class SessionController extends ChangeNotifier implements SessionPort { await clearSession(); return; } + final generation = _generation; try { final response = await _apiClient.authApi.refreshToken( refreshRequest: RefreshRequest(refreshToken: refreshToken), ); - await _adopt(response.data!); + if (generation != _generation) return; + await _adopt(response.data!, generation: generation); } catch (_) { + if (generation != _generation) return; await clearSession(); } }