feat(session): centralize session teardown and refresh

Add SessionLifecycleService to reset user-scoped cubits, analytics, socket id and account-scoped preferences on logout, 401 or account deletion. Run the same refresh after every successful auth path, including registration v1/v2, and ignore 401 responses from stale tokens so an old request cannot terminate the new user's session.

Refs: #1113
This commit is contained in:
2026-10-30 15:15:28 +03:00
parent 5f0b7e026b
commit cba2689406
16 changed files with 288 additions and 146 deletions

View File

@@ -3,6 +3,7 @@ import 'dart:developer';
import 'package:eatvio_app/core/firebase_api.dart';
import 'package:eatvio_app/core/prefs.dart';
import 'package:eatvio_app/core/prefs_keys.dart';
import 'package:eatvio_app/core/session/session_lifecycle_service.dart';
import 'package:eatvio_app/core/token_storage.dart';
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit/auth_state.dart';
@@ -17,15 +18,26 @@ class AuthCubit extends Cubit<AuthState> {
final ProfileRepository profileRepository;
final FirebaseApi firebaseApi;
final Prefs prefs;
final SessionLifecycleService sessionLifecycleService;
AuthCubit({required this.authRepository, required this.prefs, required this.profileRepository, required this.firebaseApi}) : super(const AuthState());
AuthCubit({
required this.authRepository,
required this.prefs,
required this.profileRepository,
required this.firebaseApi,
required this.sessionLifecycleService,
}) : super(const AuthState());
Future<bool> getTokenByCredentials(String email, String password) async {
emit(const AuthState.loading());
try {
final token = (await authRepository.getTokenByCredentials(email, password)).token;
final token = (await authRepository.getTokenByCredentials(
email,
password,
)).token;
emit(AuthState.loaded(token: token));
await TokenStorage.saveTokenInStorage(token);
await sessionLifecycleService.onAuthenticated();
return true;
} catch (e, st) {
await reportFrom(e, st, this);
@@ -48,7 +60,8 @@ class AuthCubit extends Cubit<AuthState> {
Future<void> registerAnonymousPushToken() async {
try {
final hasAnonymousTokenBeenSent = prefs.getBool(PrefsKeys.hasAnonymousTokenBeenSent) ?? false;
final hasAnonymousTokenBeenSent =
prefs.getBool(PrefsKeys.hasAnonymousTokenBeenSent) ?? false;
if (!hasAnonymousTokenBeenSent) {
await firebaseApi.registerAnonymousPushToken();
prefs.setBool(PrefsKeys.hasAnonymousTokenBeenSent, true);

View File

@@ -3,6 +3,7 @@ import 'package:dio/dio.dart';
import 'package:eatvio_app/core/firebase_api.dart';
import 'package:eatvio_app/core/mixins/registration_date_cache_mixin.dart';
import 'package:eatvio_app/core/prefs.dart';
import 'package:eatvio_app/core/session/session_lifecycle_service.dart';
import 'package:eatvio_app/core/token_storage.dart';
import 'package:eatvio_app/features/auth/models/oauth_verification_request_response.dart';
import 'package:eatvio_app/features/auth/presentation/bloc/oauth_cubit/oauth_state.dart';
@@ -13,10 +14,12 @@ import 'package:eatvio_app/core/error_reporting/report_from.dart';
class OAuthCubit extends Cubit<OAuthState> with RegistrationDateCacheMixin {
final AuthRepository authRepository;
final FirebaseApi firebaseApi;
final SessionLifecycleService sessionLifecycleService;
OAuthCubit({
required this.authRepository,
required this.firebaseApi,
required this.sessionLifecycleService,
required Prefs prefs,
}) : super(const OAuthState()) {
this.prefs = prefs;
@@ -40,6 +43,7 @@ class OAuthCubit extends Cubit<OAuthState> with RegistrationDateCacheMixin {
log('OAuthCubit: Got token model: ${tokenModel.token}');
await TokenStorage.saveTokenInStorage(tokenModel.token);
await sessionLifecycleService.onAuthenticated();
emit(OAuthState.loaded(token: tokenModel.token));
} on DioException catch (e, st) {
await reportFrom(e, st, this);
@@ -122,7 +126,7 @@ class OAuthCubit extends Cubit<OAuthState> with RegistrationDateCacheMixin {
clientId: clientId,
);
await TokenStorage.saveTokenInStorage(token);
await firebaseApi.registerPushToken();
await sessionLifecycleService.onAuthenticated();
cacheRegistrationDate(DateTime.now());
emit(OAuthState.loaded(token: token));
} on DioException catch (e, st) {
@@ -241,7 +245,7 @@ class OAuthCubit extends Cubit<OAuthState> with RegistrationDateCacheMixin {
'OAuthCubit: OAuth verification confirmed successfully, token received',
);
await TokenStorage.saveTokenInStorage(response.token);
await firebaseApi.registerPushToken();
await sessionLifecycleService.onAuthenticated();
emit(OAuthState.loaded(token: response.token));
} catch (e, st) {
await reportFrom(e, st, this);