fix(audit): low-tail cleanup — session/logout/unread/presence/forward
CI / Build & Quality Checks (push) Successful in 10m45s
CI / Trigger Desktop Build (push) Successful in 14s

Clears the clean 🟡 remainders from the feature audit (gate-green, 677 tests):
- F3: getFallbackSession prefers the session-blob/legacy source with the later
  expiresAt (a downgrade→upgrade could boot on a stale blob's dead token).
- F6: server-forced logout (SessionLoggedOut) now mirrors logoutClient —
  pushSessionToSW() + best-effort revokeOidcTokens for OIDC sessions (the search
  plaintext wipe was already added).
- N5: deleteUnreadInfo parent fallback `?? roomId` → `?? []` (latently spread the
  roomId string into chars).
- P10: useUserPresence re-seeds when the User object appears after first render.
- forward: strip m.mentions so forwarding doesn't re-ping the original mentions.

Left open: F5 (OIDC expiry not reachable in persistTokens), N6/H10/D7 (minor /
runtime-verify). See LOTUS_TODO.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 22:57:09 -04:00
parent b7788cc79c
commit 21276a47fc
6 changed files with 50 additions and 2 deletions
+12
View File
@@ -44,6 +44,8 @@ import { stopPropagation } from '../../utils/keyboard';
import { SyncStatus } from './SyncStatus';
import { AuthMetadataProvider } from '../../hooks/useAuthMetadata';
import { getFallbackSession, removeFallbackSession } from '../../state/sessions';
import { pushSessionToSW } from '../../../sw-session';
import { revokeOidcTokens } from '../../../client/oidcLogout';
import { useSessionSync } from '../../hooks/useSessionSync';
import { installCryptoDiagLog } from '../../utils/cryptoDiagLog';
import { AutoDiscovery } from './AutoDiscovery';
@@ -143,7 +145,17 @@ function ClientRootOptions({ mx }: { mx?: MatrixClient }) {
const useLogoutListener = (mx?: MatrixClient) => {
useEffect(() => {
const handleLogout: HttpApiEventHandlerMap[HttpApiEvent.SessionLoggedOut] = async () => {
// Clear the SW's cached bearer token so it stops attaching the now-revoked
// token to media fetches (mirrors the manual logoutClient path).
pushSessionToSW();
mx?.stopClient();
// Best-effort issuer revocation for OIDC sessions (the token is already
// server-revoked here, but revoke the refresh token too). Before we drop
// the stored session below.
const loggedOutSession = getFallbackSession();
if (loggedOutSession?.oidc) {
await revokeOidcTokens(loggedOutSession).catch(() => undefined);
}
await mx?.clearStores();
// The opt-in local search index holds DECRYPTED message plaintext. Wipe it
// on server-forced logout too (token expiry / remote sign-out / password