45 lines
1.8 KiB
Dart
45 lines
1.8 KiB
Dart
import 'dart:developer';
|
|
|
|
import '../../../session/session.dart';
|
|
import '../../../session/session_lifecycle.dart';
|
|
import '../../../session/session_manager.dart';
|
|
import '../../errors/auth_exception.dart';
|
|
import '../queries/auth_verify/auth_verify.dart';
|
|
|
|
/// Credential probe. A server-side password rotation forces a re-login on the
|
|
/// next cold start even when the bearer token would still be accepted.
|
|
/// Another stored account then takes over.
|
|
class SessionValidator {
|
|
static Future<void> probeStored({
|
|
required Future<void> Function(String? nextAccountId) onInvalidated,
|
|
}) async {
|
|
final session = SessionManager().current;
|
|
// The probes use their own dio (bypassing the demo interceptor), so a demo
|
|
// session must be skipped or its missing token would 401 into a logout.
|
|
if (session == null || session.isDemo) return;
|
|
final epoch = SessionManager().sessionEpoch;
|
|
try {
|
|
switch (session) {
|
|
case CredentialSession(:final username, :final password):
|
|
await AuthVerify().run(username: username, password: password);
|
|
}
|
|
} on AuthException catch (e) {
|
|
if (e.statusCode != 401) return;
|
|
// The probed account is no longer the active one; the 401 must not
|
|
// sign out whoever took over meanwhile.
|
|
if (!SessionManager().isCurrentSession(epoch)) return;
|
|
log('MC: stored session rejected — forcing re-login');
|
|
final next = await SessionLifecycle.signOut(
|
|
notice: switch (session) {
|
|
CredentialSession() =>
|
|
'Deine Zugangsdaten wurden vom Server abgelehnt. Vermutlich '
|
|
'wurde dein Passwort geändert. Bitte melde dich erneut an.',
|
|
},
|
|
);
|
|
await onInvalidated(next);
|
|
} catch (e) {
|
|
log('MC: background session check failed (transient): $e');
|
|
}
|
|
}
|
|
}
|