-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathrouter_logout_session_guard.dart
More file actions
89 lines (72 loc) · 2.45 KB
/
Copy pathrouter_logout_session_guard.dart
File metadata and controls
89 lines (72 loc) · 2.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import 'dart:async';
import 'package:logging/logging.dart';
import 'package:webtrit_phone/common/common.dart';
import 'session_guard.dart';
typedef SessionGuardCallback = FutureOr<void> Function(Exception e);
final _log = Logger('RouterLogoutSessionGuard');
/// A [SessionGuard] that triggers logout when the backend signals
/// an invalid session (e.g. `422` with `code=refresh_token_invalid`).
///
/// Features:
/// - Handles only the first unauthorized event; ignores subsequent ones.
/// - Optionally runs [onPreLogout] before [performLogout].
/// - Implements [Disposable] to stop handling after disposal.
///
/// Typical usage:
/// - Attach to repositories or API clients to enforce automatic logout
/// when the session becomes invalid.
class RouterLogoutSessionGuard implements SessionGuard, Disposable {
/// Creates a new [RouterLogoutSessionGuard] instance.
RouterLogoutSessionGuard({required this.performLogout, this.onPreLogout});
/// Function that performs the actual logout (e.g. dispatching a logout event).
/// 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. Receives the same
/// unauthorized [Exception] passed to [performLogout].
final SessionGuardCallback? onPreLogout;
bool _handled = false;
bool _disposed = false;
@override
void onUnauthorized(Exception e) {
if (!_markHandled()) {
_log.finest('Skip: unauthorized already handled');
return;
}
Future.microtask(() async {
if (_disposed) {
_log.finest('Skip: already disposed');
return;
}
_log.warning('Unauthorized access detected: ${e.toString()}');
await _runHookSafe(e);
await _runLogoutSafe(e);
});
}
bool _markHandled() {
if (_handled) return false;
_handled = true;
return true;
}
Future<void> _runHookSafe(Exception e) async {
final hook = onPreLogout;
if (hook == null) return;
try {
await hook(e);
} catch (err, st) {
_log.warning('onBeforeLogout failed', err, st);
}
}
Future<void> _runLogoutSafe(Exception e) async {
try {
await performLogout(e);
} catch (err, st) {
_log.severe('logoutLocal failed', err, st);
}
}
@override
Future<void> dispose() async {
_disposed = true;
}
}