From 7e562c6cb53ebeb8a3c3c06cf0b95e72f16de0a6 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Sat, 11 Jul 2026 20:57:30 +0200 Subject: [PATCH] fix(e2ee): clear the device's private key on logout (user-approved: every logout) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves the deferred clearDevice security gap: logout never removed the E2EE private key from IndexedDB, so on a shared machine the conversations stayed decryptable after sign-out (ChatDock auto-unlocks from the persisted key next visit). Per the user's decision (clear on EVERY logout), NavSidebar.logout() now awaits e2ee.clearDevice(me.id) before reload — guarded so an IndexedDB failure (e.g. private mode) can never block the logout. Re-entering the message passphrase is required on the next visit. Resolves the Phase-1 NEEDS-DECISION #3 (clearDevice) + retires the last e2ee unused export (knip now only flags loadDefaultViewFilters). tsc green, re-review clean (found + fixed an idb-open throw path blocking logout). --- frontend/src/components/NavSidebar.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frontend/src/components/NavSidebar.tsx b/frontend/src/components/NavSidebar.tsx index 2634a61..c3dab6d 100644 --- a/frontend/src/components/NavSidebar.tsx +++ b/frontend/src/components/NavSidebar.tsx @@ -23,6 +23,7 @@ import { UserPlus, } from "lucide-react"; import { api, clearActiveAccount, setActiveAccount, type Me } from "../lib/api"; +import * as e2ee from "../lib/e2ee"; import { useLiveQuery } from "../lib/useLiveQuery"; import { getUnreadCount, subscribe } from "../lib/notifications"; import type { Page } from "../lib/urlState"; @@ -103,6 +104,14 @@ export default function NavSidebar({ } catch { /* clear locally and reload regardless */ } + // Forget this device's E2EE private key on sign-out: it's otherwise persisted in IndexedDB + // and auto-unlocks on the next visit, so on a shared machine the conversations would stay + // decryptable after logout. Re-entering the message passphrase is required next time. + try { + await e2ee.clearDevice(me.id); + } catch { + /* IndexedDB unavailable (e.g. private mode) — never let it block the logout + reload */ + } clearActiveAccount(); location.reload(); }