cipher/public/js/accounts.js
maverick bd112d07fe Fix: namespace encrypted store per-account so two accounts can coexist on one browser
Root cause of 'two users can't chat': all accounts on one browser shared a
single IndexedDB, so the second registration overwrote the first's Signal
identity keys, corrupting both. Verified via two-isolated-client integration
test (real crypto.js + store.js) that the protocol itself is correct.

- store.js: useAccount(username) namespaces IndexedDB as cipher:<username>;
  keys generated at registration now persist under the right account
- accounts.js: localStorage-backed account registry + active pointer
- app.js: multi-account boot/activate, account-switch/add/logout menu,
  error toasts on send/decrypt failures (no more silent failures)
- login guards against missing on-device keys (multi-device linking still TODO)
- SW cache v2->v3 + precache accounts.js

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 02:48:00 +10:00

39 lines
1.1 KiB
JavaScript

// Account registry, stored in localStorage (global to the origin, unlike the
// per-account IndexedDB). Tracks which accounts have signed in on this browser
// and which one is currently active. Tokens live here; private keys never do.
const KEY = "cipher.accounts";
const ACTIVE = "cipher.activeUser";
export function list() {
try {
return JSON.parse(localStorage.getItem(KEY) || "[]");
} catch {
return [];
}
}
export function save(acct) {
// acct: { userId, deviceId, username, displayName, token }
const others = list().filter((a) => a.username !== acct.username);
others.push(acct);
localStorage.setItem(KEY, JSON.stringify(others));
}
export function remove(username) {
localStorage.setItem(KEY, JSON.stringify(list().filter((a) => a.username !== username)));
if (getActiveUsername() === username) localStorage.removeItem(ACTIVE);
}
export function setActive(username) {
localStorage.setItem(ACTIVE, username);
}
export function getActiveUsername() {
return localStorage.getItem(ACTIVE);
}
export function getActive() {
const u = getActiveUsername();
return u ? list().find((a) => a.username === u) || null : null;
}