Files
pyramid/lib/core/soft_logout_guard.dart
T
Bernd Steckmeister 891f3489bd fix: Soft-Logout darf nie mehr destruktiv enden (onSoftLogout-Guard)
Qualitäts-Review-Befund (b): Der Refresh-Token-Fix (9dc83f7) war richtig
analysiert, aber unvollständig – drei Restlücken gegen das matrix-SDK
6.2.0 verifiziert und geschlossen:

1. Client._handleSoftLogout ruft bei JEDEM Refresh-Fehler logout() auf,
   und logout() macht 'finally clear()' – die lokale Session inkl.
   Krypto-Schlüssel wird selbst bei einem transienten Netzwerkfehler
   zerstört. Mit refreshToken:true laufen Access-Tokens künftig ab,
   Refreshes werden Routine – das Restrisiko wäre also GESTIEGEN.
   Fix: eigener onSoftLogout-Handler (soft_logout_guard.dart) mit
   3 Versuchen, der nie wirft → SDK ruft nie logout() auf; bei
   Dauerfehler bleibt der Client soft-logged-out und der nächste
   Sync versucht es erneut (CLAUDE.md: Fehlerpfade dürfen NIE in
   einem stillen Logout enden).

2. Das Push-Hintergrund-Isolate (background_push.dart) teilt sich die
   pyramid.sqlite mit der Haupt-App; getEventByPushNotification()
   ruft ensureNotSoftLoggedOut() auf. Ohne Guard hätte ein
   Refresh-Fehler im Isolate (inkl. Token-Rotations-Race mit der
   Haupt-App) die Session der GESAMTEN App gelöscht. Guard auch dort
   gesetzt; das Race heilt der Retry, weil refreshAccessToken() den
   Token bei jedem Versuch frisch aus der DB liest.

3. isLoggedInProvider behandelte softLoggedOut wie ausgeloggt – die UI
   wäre bei jedem Routine-Token-Refresh kurz auf den Login-Screen
   gesprungen (für Nutzer nicht von einem 'Random Logout' zu
   unterscheiden). Jetzt zählt nur echtes loggedOut.

UNGETESTET (Pi) – gehört zum selben ausstehenden PC-Praxistest wie
9dc83f7: Login → Nachrichten → Logout → Neu-Login → alte Nachrichten
lesbar; zusätzlich auth_log.txt auf die neuen Guard-Einträge prüfen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:42:15 +02:00

42 lines
1.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:matrix/matrix.dart';
import 'package:pyramid/core/auth_log.dart';
/// Wendet einen Soft-Logout durch Token-Refresh ab, ohne JE in einen
/// destruktiven Logout zu eskalieren.
///
/// Hintergrund: Ohne gesetztes `Client.onSoftLogout` behandelt das matrix-SDK
/// einen fehlgeschlagenen Refresh so: catch → `logout()` → `finally clear()` —
/// die lokale Session inkl. Krypto-Schlüssel wird also selbst dann zerstört,
/// wenn der Refresh nur an einem transienten Netzwerkfehler scheiterte
/// (Gerät offline im Moment des Token-Ablaufs) oder am Rotations-Race mit dem
/// Push-Hintergrund-Isolate (beide teilen sich die DB; refreshAccessToken()
/// liest den Refresh-Token bei jedem Versuch frisch aus der DB, deshalb heilt
/// ein Retry dieses Race). Wirft dieser Handler nicht, ruft das SDK auch kein
/// `logout()` auf (Client._handleSoftLogout, matrix 6.2.0).
///
/// Regel aus CLAUDE.md: Fehlerpfade dürfen NIE in einem stillen Logout enden.
/// Schlägt der Refresh dauerhaft fehl, bleibt der Client im Zustand
/// soft-logged-out; der nächste Sync/API-Aufruf stößt den Refresh erneut an.
Future<void> guardedSoftLogoutRefresh(Client client) async {
for (var attempt = 1; attempt <= 3; attempt++) {
try {
await client.refreshAccessToken();
await AuthLog.write(
'Soft-Logout durch Token-Refresh abgewendet (Versuch $attempt)',
);
return;
} catch (e) {
await AuthLog.write(
'Soft-Logout: Token-Refresh fehlgeschlagen (Versuch $attempt/3): $e',
);
if (attempt < 3) {
await Future.delayed(Duration(seconds: 2 * attempt));
}
}
}
await AuthLog.write(
'Soft-Logout: Refresh dauerhaft fehlgeschlagen Session wird NICHT '
'zerstört, nächster Sync/API-Aufruf versucht es erneut.',
);
}