Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lib/app/notifications/models/notification.dart
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,15 @@ class SessionExpiredNotification extends MessageNotification {
}
}

class AccountNotFoundNotification extends MessageNotification {
const AccountNotFoundNotification();

@override
String l10n(BuildContext context) {
return context.l10n.notifications_errorSnackBar_accountNotFound;
}
}

class DeleteAccountNotSupportedNotification extends MessageNotification {
const DeleteAccountNotSupportedNotification();

Expand Down
13 changes: 9 additions & 4 deletions lib/app/router/main_shell.dart
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,16 @@ class _MainShellState extends State<MainShell> with WidgetsBindingObserver {
final notificationsBloc = context.read<NotificationsBloc>();

_sessionGuard = RouterLogoutSessionGuard(
performLogout: () {
_appBloc.add(const AppLogoutRequested(reason: AppLogoutReason.serverRejection));
performLogout: (e) {
final reason = e is UserNotFoundException ? AppLogoutReason.userNotFound : AppLogoutReason.serverRejection;
_appBloc.add(AppLogoutRequested(reason: reason));
},
Comment thread
SERDUN marked this conversation as resolved.
Outdated
onPreLogout: () {
notificationsBloc.add(NotificationsSubmitted(SessionExpiredNotification()));
onPreLogout: (e) {
Comment thread
SERDUN marked this conversation as resolved.
Outdated
notificationsBloc.add(
NotificationsSubmitted(
e is UserNotFoundException ? const AccountNotFoundNotification() : const SessionExpiredNotification(),
),
);
},
);
}
Expand Down
23 changes: 13 additions & 10 deletions lib/app/session/router_logout_session_guard.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'package:webtrit_phone/common/common.dart';

import 'session_guard.dart';

typedef AsyncVoidCallback = FutureOr<void> Function();
typedef SessionGuardCallback = FutureOr<void> Function(Exception e);

final _log = Logger('RouterLogoutSessionGuard');

Expand All @@ -26,11 +26,14 @@ class RouterLogoutSessionGuard implements SessionGuard, Disposable {
RouterLogoutSessionGuard({required this.performLogout, this.onPreLogout});

/// Function that performs the actual logout (e.g. dispatching a logout event).
final AsyncVoidCallback performLogout;
/// Receives the unauthorized [Exception] so callers can tailor the logout
/// (e.g. distinguish an expired session from a deleted account).
final SessionGuardCallback performLogout;

/// Optional hook executed before [performLogout].
/// Useful for cleaning up resources or saving state.
final AsyncVoidCallback? onPreLogout;
/// Useful for cleaning up resources or saving state. Receives the same
/// unauthorized [Exception] passed to [performLogout].
final SessionGuardCallback? onPreLogout;

bool _handled = false;
bool _disposed = false;
Expand All @@ -50,8 +53,8 @@ class RouterLogoutSessionGuard implements SessionGuard, Disposable {

_log.warning('Unauthorized access detected: ${e.toString()}');

await _runHookSafe();
await _runLogoutSafe();
await _runHookSafe(e);
await _runLogoutSafe(e);
});
}

Expand All @@ -61,19 +64,19 @@ class RouterLogoutSessionGuard implements SessionGuard, Disposable {
return true;
}

Future<void> _runHookSafe() async {
Future<void> _runHookSafe(Exception e) async {
final hook = onPreLogout;
if (hook == null) return;
try {
await hook();
await hook(e);
} catch (err, st) {
_log.warning('onBeforeLogout failed', err, st);
Comment thread
Copilot marked this conversation as resolved.
Outdated
}
}

Future<void> _runLogoutSafe() async {
Future<void> _runLogoutSafe(Exception e) async {
try {
await performLogout();
await performLogout(e);
} catch (err, st) {
_log.severe('logoutLocal failed', err, st);
}
Expand Down
5 changes: 3 additions & 2 deletions lib/blocs/app/app_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,9 @@ class AppBloc extends Bloc<AppEvent, AppState> {
final currentSession = sessionRepository.getCurrent();

// Determine if we should attempt to revoke the session on the server.
// We skip this only for 'sessionMissed' because the socket error (4201)
// guarantees the session is already terminated.
// We skip this for 'sessionMissed' because the socket error (4201)
// guarantees the session is already terminated, and for 'userNotFound'
// because the account no longer exists (the revoke would just 404 again).
final shouldRevokeRemote = reason == AppLogoutReason.userRequest || reason == AppLogoutReason.serverRejection;

if (shouldRevokeRemote && currentSession.isLoggedIn) {
Expand Down
5 changes: 5 additions & 0 deletions lib/blocs/app/app_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ enum AppLogoutReason {
/// The server explicitly rejected a request due to authentication.
/// Requires both remote session revocation and local data cleanup.
serverRejection,

/// The account no longer exists on the server (404 UserNotFoundException),
/// e.g. it was deactivated or removed. Terminal; requires only local data
/// cleanup since the remote user record is already gone.
userNotFound,
}

sealed class AppEvent extends Equatable {
Expand Down
6 changes: 6 additions & 0 deletions lib/l10n/app_localizations.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2526,6 +2526,12 @@ abstract class AppLocalizations {
/// **'Your session has expired. Please log in again.'**
String get notifications_errorSnackBar_sessionExpired;

/// Shown in a notification or snackbar when the backend returns 404 (user not found) for the current account, e.g. it was deactivated or removed on the server. The user is logged out; advise them to contact their administrator.
///
/// In en, this message translates to:
/// **'Your account was not found. It may have been deactivated or removed. Please contact your administrator.'**
String get notifications_errorSnackBar_accountNotFound;

/// Shown in a notification or snackbar when the app fails to connect to the WebTrit core and is attempting automatic reconnection. Context: occurs when the signaling/WebSocket connection cannot be established due to network outages, server unreachability, TLS/handshake failures, authentication errors (expired/invalid tokens), or firewall/VPN restrictions. Suggest the user check their network, retry, or reauthenticate if the problem persists.
///
/// In en, this message translates to:
Expand Down
4 changes: 4 additions & 0 deletions lib/l10n/app_localizations_en.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1350,6 +1350,10 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get notifications_errorSnackBar_sessionExpired => 'Your session has expired. Please log in again.';

@override
String get notifications_errorSnackBar_accountNotFound =>
'Your account was not found. It may have been deactivated or removed. Please contact your administrator.';

@override
String get notifications_errorSnackBar_SignalingConnectFailed => 'Connecting to the core failed, trying to reconnect';

Expand Down
4 changes: 4 additions & 0 deletions lib/l10n/app_localizations_it.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1368,6 +1368,10 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get notifications_errorSnackBar_sessionExpired => 'La tua sessione è scaduta. Accedi di nuovo.';

@override
String get notifications_errorSnackBar_accountNotFound =>
'Il tuo account non è stato trovato. Potrebbe essere stato disattivato o rimosso. Contatta il tuo amministratore.';

@override
String get notifications_errorSnackBar_SignalingConnectFailed =>
'Connessione al server non riuscita, tentativo di riconnessione in corso';
Expand Down
4 changes: 4 additions & 0 deletions lib/l10n/app_localizations_uk.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1372,6 +1372,10 @@ class AppLocalizationsUk extends AppLocalizations {
String get notifications_errorSnackBar_sessionExpired =>
'Термін дії вашої сесії закінчився. Будь ласка, увійдіть знову.';

@override
String get notifications_errorSnackBar_accountNotFound =>
'Ваш обліковий запис не знайдено. Можливо, його деактивовано або видалено. Зверніться до адміністратора.';

@override
String get notifications_errorSnackBar_SignalingConnectFailed => 'Підключення до ядра не вдалося, спроба з\'єднання';

Expand Down
4 changes: 4 additions & 0 deletions lib/l10n/arb/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,10 @@
"@notifications_errorSnackBar_sessionExpired": {
"description": "Shown in a notification or snackbar when the user's authentication session has expired and re-authentication is required. Typical causes: access or refresh token expiration/revocation, failed token refresh, or the backend invalidating the session. Advise the user to sign in again to restore full functionality."
},
"notifications_errorSnackBar_accountNotFound": "Your account was not found. It may have been deactivated or removed. Please contact your administrator.",
"@notifications_errorSnackBar_accountNotFound": {
"description": "Shown in a notification or snackbar when the backend returns 404 (user not found) for the current account, e.g. it was deactivated or removed on the server. The user is logged out; advise them to contact their administrator."
},
"notifications_errorSnackBar_SignalingConnectFailed": "Connecting to the core failed, trying to reconnect",
"@notifications_errorSnackBar_SignalingConnectFailed": {
"description": "Shown in a notification or snackbar when the app fails to connect to the WebTrit core and is attempting automatic reconnection. Context: occurs when the signaling/WebSocket connection cannot be established due to network outages, server unreachability, TLS/handshake failures, authentication errors (expired/invalid tokens), or firewall/VPN restrictions. Suggest the user check their network, retry, or reauthenticate if the problem persists."
Expand Down
4 changes: 4 additions & 0 deletions lib/l10n/arb/app_it.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,10 @@
"@notifications_errorSnackBar_sessionExpired": {
"description": "Shown in a notification or snackbar when the user's authentication session has expired and re-authentication is required. Typical causes: access or refresh token expiration/revocation, failed token refresh, or the backend invalidating the session. Advise the user to sign in again to restore full functionality."
},
"notifications_errorSnackBar_accountNotFound": "Il tuo account non è stato trovato. Potrebbe essere stato disattivato o rimosso. Contatta il tuo amministratore.",
"@notifications_errorSnackBar_accountNotFound": {
"description": "Shown in a notification or snackbar when the backend returns 404 (user not found) for the current account, e.g. it was deactivated or removed on the server. The user is logged out; advise them to contact their administrator."
},
"notifications_errorSnackBar_SignalingConnectFailed": "Connessione al server non riuscita, tentativo di riconnessione in corso",
"@notifications_errorSnackBar_SignalingConnectFailed": {
"description": "Shown in a notification or snackbar when the app fails to connect to the WebTrit core and is attempting automatic reconnection. Context: occurs when the signaling/WebSocket connection cannot be established due to network outages, server unreachability, TLS/handshake failures, authentication errors (expired/invalid tokens), or firewall/VPN restrictions. Suggest the user check their network, retry, or reauthenticate if the problem persists."
Expand Down
4 changes: 4 additions & 0 deletions lib/l10n/arb/app_uk.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,10 @@
"@notifications_errorSnackBar_sessionExpired": {
"description": "Shown in a notification or snackbar when the user's authentication session has expired and re-authentication is required. Typical causes: access or refresh token expiration/revocation, failed token refresh, or the backend invalidating the session. Advise the user to sign in again to restore full functionality."
},
"notifications_errorSnackBar_accountNotFound": "Ваш обліковий запис не знайдено. Можливо, його деактивовано або видалено. Зверніться до адміністратора.",
"@notifications_errorSnackBar_accountNotFound": {
"description": "Shown in a notification or snackbar when the backend returns 404 (user not found) for the current account, e.g. it was deactivated or removed on the server. The user is logged out; advise them to contact their administrator."
},
"notifications_errorSnackBar_SignalingConnectFailed": "Підключення до ядра не вдалося, спроба з'єднання",
"@notifications_errorSnackBar_SignalingConnectFailed": {
"description": "Shown in a notification or snackbar when the app fails to connect to the WebTrit core and is attempting automatic reconnection. Context: occurs when the signaling/WebSocket connection cannot be established due to network outages, server unreachability, TLS/handshake failures, authentication errors (expired/invalid tokens), or firewall/VPN restrictions. Suggest the user check their network, retry, or reauthenticate if the problem persists."
Expand Down
Loading