Files
ChatApp/apps/desktop/src/lib/memoryWipe.ts
T
byGalax 6c6828006b feat(P6B.T9): PIN-Idle-Auto-Lock setting + idle watcher
Adds opt-in (default OFF) auto-lock: after X minutes of no user input
the app calls signOut() (full memory wipe + PIN re-entry on next open).
Settings dropdown (Aus / 5 / 15 / 30 / 60 min) lives in SecurityCenter
below the existing wipe-on-close toggle. The idle timer is mounted in
AppShell via useIdleAutoLock; activity events are throttled to 1 Hz to
avoid timer thrash on rapid mouse movement. The localStorage key is
added to PRESERVE_LOCAL_STORAGE so a wipe never silently disables the
feature.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 23:59:27 +02:00

57 lines
2.1 KiB
TypeScript

import { clearConvKeyCache } from '@chat-app/shared/chat';
import { devLocalSecretStore } from './secretStore';
// Aggressively scrub local crypto + chat caches on sign-out (and on
// app-close if the user opted in). Preserves things that aren't sensitive
// and would be annoying to lose (theme, locale, install-id).
//
// We can't enumerate IndexedDB names without async + the indexedDB API,
// so we list the ones we know about explicitly. Adding a new local store
// later? Append to LOCAL_DBS.
const LOCAL_DBS = ['soundboard', 'message-cache', 'chatapp-attachments'];
const PRESERVE_LOCAL_STORAGE = new Set([
'chatapp.theme',
'chatapp.locale',
'chatapp.installId',
'chatapp.wipeOnClose.v1',
'chatapp.autoLockMinutes.v1',
'i18nextLng',
]);
export async function wipeLocalState(userId: string | null): Promise<void> {
// 1. Per-conversation key cache (in-memory).
try { clearConvKeyCache(); } catch { /* never throws but be defensive */ }
// 2. Stronghold / secret-store: drop the user-priv blob for this user.
if (userId) {
try { await devLocalSecretStore.removeSecret('chatapp.userpriv.' + userId); }
catch (err) { console.warn('[wipe] userpriv remove failed', err); }
}
// 3. localStorage — preserve only the explicit whitelist.
try {
const keysToDrop: string[] = [];
for (let i = 0; i < window.localStorage.length; i++) {
const k = window.localStorage.key(i);
if (k && !PRESERVE_LOCAL_STORAGE.has(k)) keysToDrop.push(k);
}
for (const k of keysToDrop) window.localStorage.removeItem(k);
} catch (err) { console.warn('[wipe] localStorage clear failed', err); }
// 4. sessionStorage — always full.
try { window.sessionStorage.clear(); } catch { /* ignored */ }
// 5. IndexedDB — delete known databases. Resolves even when blocked so
// we don't hang sign-out forever.
await Promise.allSettled(LOCAL_DBS.map((name) => new Promise<void>((resolve) => {
try {
const req = window.indexedDB.deleteDatabase(name);
req.onsuccess = () => resolve();
req.onerror = () => resolve();
req.onblocked = () => resolve();
} catch { resolve(); }
})));
}