feat(auth): Enhance logout flow with comprehensive data clearing
This commit is contained in:
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user