feat(auth): Enhance logout flow with comprehensive data clearing

This commit is contained in:
cogwheel0
2025-11-12 13:44:05 +05:30
parent 45135d9185
commit ecf760d9ec
6 changed files with 45 additions and 11 deletions

View File

@@ -174,7 +174,6 @@ class ApiAuthInterceptor extends Interceptor {
/// Clear auth token and notify callbacks /// Clear auth token and notify callbacks
/// Note: This should only be called for explicit logout, not for connection errors /// Note: This should only be called for explicit logout, not for connection errors
void _clearAuthToken() { void _clearAuthToken() {
_authToken = null; _authToken = null;
final future = onTokenInvalidated?.call(); final future = onTokenInvalidated?.call();

View File

@@ -100,11 +100,11 @@ class AuthCacheManager {
DebugLogger.storage('Cache entry cleared: $key'); DebugLogger.storage('Cache entry cleared: $key');
} }
/// Clear all auth-related cache /// Clear all auth-related cache including server configs
void clearAuthCache() { void clearAuthCache() {
_cache.clear(); _cache.clear();
_cacheTimestamps.clear(); _cacheTimestamps.clear();
DebugLogger.storage('All auth cache cleared'); DebugLogger.storage('All auth cache cleared (including server configs and custom headers)');
} }
/// Clear expired cache entries /// Clear expired cache entries

View File

@@ -677,7 +677,7 @@ class AuthStateManager extends _$AuthStateManager {
} }
} }
/// Logout user /// Logout user and clear all data including server configs and custom headers
Future<void> logout() async { Future<void> logout() async {
_update( _update(
(current) => (current) =>
@@ -699,14 +699,20 @@ class AuthStateManager extends _$AuthStateManager {
} }
} }
// Clear all local auth data // Clear all local auth data (including server configs with custom headers)
final storage = ref.read(optimizedStorageServiceProvider); final storage = ref.read(optimizedStorageServiceProvider);
await storage.clearAuthData(); await storage.clearAuthData();
_updateApiServiceToken(null); _updateApiServiceToken(null);
// Clear active server to force return to server connection page // Clear active server to force return to server connection page
await storage.setActiveServerId(null); await storage.setActiveServerId(null);
// Invalidate all auth-related providers to clear cached data
ref.invalidate(activeServerProvider); ref.invalidate(activeServerProvider);
ref.invalidate(serverConfigsProvider);
// Clear auth cache manager
_cacheManager.clearAuthCache();
// Update state // Update state
_update( _update(
@@ -719,7 +725,7 @@ class AuthStateManager extends _$AuthStateManager {
), ),
); );
DebugLogger.auth('Logout complete'); DebugLogger.auth('Logout complete - all data cleared including server configs and custom headers');
} catch (e, stack) { } catch (e, stack) {
DebugLogger.error( DebugLogger.error(
'logout-failed', 'logout-failed',
@@ -729,8 +735,19 @@ class AuthStateManager extends _$AuthStateManager {
); );
// Even if logout fails, clear local state where possible // Even if logout fails, clear local state where possible
final storage = ref.read(optimizedStorageServiceProvider); final storage = ref.read(optimizedStorageServiceProvider);
try {
await storage.clearAuthData();
} catch (clearError) {
DebugLogger.error(
'logout-clear-failed',
scope: 'auth/state',
error: clearError,
);
}
await storage.setActiveServerId(null); await storage.setActiveServerId(null);
ref.invalidate(activeServerProvider); ref.invalidate(activeServerProvider);
ref.invalidate(serverConfigsProvider);
_cacheManager.clearAuthCache();
_update( _update(
(current) => current.copyWith( (current) => current.copyWith(
@@ -739,8 +756,8 @@ class AuthStateManager extends _$AuthStateManager {
clearToken: true, clearToken: true,
clearUser: true, clearUser: true,
error: error:
'Logout error: $e. Secure credentials may remain stored; ' 'Logout error: $e. Some data may remain stored; '
'please clear them from your device keychain.', 'please clear app data from your device settings if needed.',
), ),
); );
_updateApiServiceToken(null); _updateApiServiceToken(null);

View File

@@ -395,11 +395,15 @@ class OptimizedStorageService {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Batch operations // Batch operations
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// Clear all authentication-related data including credentials, tokens,
/// server configurations, and custom headers
Future<void> clearAuthData() async { Future<void> clearAuthData() async {
await Future.wait([ await Future.wait([
deleteAuthToken(), deleteAuthToken(),
deleteSavedCredentials(), deleteSavedCredentials(),
_preferencesBox.delete(_activeServerIdKey), _preferencesBox.delete(_activeServerIdKey),
// Clear server configurations (which include custom headers)
_secureCredentialStorage.clearAll(),
]); ]);
_cache.removeWhere( _cache.removeWhere(
@@ -416,7 +420,7 @@ class OptimizedStorageService {
); );
DebugLogger.log( DebugLogger.log(
'Auth data cleared in batch operation', 'Auth data cleared in batch operation (including server configs and custom headers)',
scope: 'storage/optimized', scope: 'storage/optimized',
); );
} }

View File

@@ -280,11 +280,12 @@ class SecureCredentialStorage {
} }
} }
/// Clear all secure data /// Clear all secure data including credentials, tokens, and server configurations
/// (which contain custom headers)
Future<void> clearAll() async { Future<void> clearAll() async {
try { try {
await _secureStorage.deleteAll(); await _secureStorage.deleteAll();
DebugLogger.storage('clear-ok', scope: 'credentials'); DebugLogger.storage('clear-ok (all secure data including server configs with custom headers)', scope: 'credentials');
} catch (e) { } catch (e) {
DebugLogger.error('clear-failed', scope: 'credentials', error: e); DebugLogger.error('clear-failed', scope: 'credentials', error: e);
} }

View File

@@ -12,6 +12,7 @@ import '../../../core/widgets/error_boundary.dart';
import '../../../l10n/app_localizations.dart'; import '../../../l10n/app_localizations.dart';
import '../../../shared/theme/theme_extensions.dart'; import '../../../shared/theme/theme_extensions.dart';
import '../../../shared/widgets/conduit_components.dart'; import '../../../shared/widgets/conduit_components.dart';
import '../../../shared/widgets/themed_dialogs.dart';
import '../providers/unified_auth_providers.dart'; import '../providers/unified_auth_providers.dart';
class ConnectionIssuePage extends ConsumerStatefulWidget { class ConnectionIssuePage extends ConsumerStatefulWidget {
@@ -248,6 +249,18 @@ class _ConnectionIssuePageState extends ConsumerState<ConnectionIssuePage> {
} }
Future<void> _logout(AppLocalizations l10n) async { Future<void> _logout(AppLocalizations l10n) async {
// Show confirmation dialog before logging out
final confirm = await ThemedDialogs.confirm(
context,
title: l10n.signOut,
message: l10n.endYourSession,
confirmText: l10n.signOut,
isDestructive: true,
);
if (!mounted) return;
if (!confirm) return;
setState(() { setState(() {
_isLoggingOut = true; _isLoggingOut = true;
_statusMessage = null; _statusMessage = null;