fix(messages): address S2 review — partner key refresh + reactive me
- On a decrypt failure, refresh the partner's cached public key (and drop the stale derived conversation key) once per pass and retry. A passphrase reset rotates a keypair, so a partner who cached the old public key would otherwise encrypt undecryptable messages until a reload; the first failed decrypt now self-heals reads AND future sends. (pubCache was assumed set-once.) - KeyGate reads has_password via a reactive useQuery(['me'], enabled:false) passive observer instead of a one-shot getQueryData, so the reset password field stays correct even if me resolves after mount. - Document the remaining multi-device staleness (a reset on another device leaves this one 'ready' with the dead key) as a known limitation in useKeyState.
This commit is contained in:
@@ -22,8 +22,10 @@ export default function KeyGate({ meId, compact = false }: { meId: number; compa
|
||||
});
|
||||
const configured = keyQ.data?.configured ?? false;
|
||||
// Password accounts must re-verify to reset (guards a hijacked session); Google-only accounts
|
||||
// have no password to check. Read from the App-owned cache — KeyGate is always rendered signed in.
|
||||
const hasPassword = qc.getQueryData<{ has_password?: boolean }>(["me"])?.has_password ?? false;
|
||||
// have no password to check. Reactive passive observer of the App-owned ["me"] query (enabled:
|
||||
// false — App does the fetching), so hasPassword stays correct even if me resolves after mount.
|
||||
const meQ = useQuery({ queryKey: ["me"], queryFn: api.me, enabled: false });
|
||||
const hasPassword = meQ.data?.has_password ?? false;
|
||||
|
||||
const [view, setView] = useState<"gate" | "reset">("gate");
|
||||
const [pass, setPass] = useState("");
|
||||
|
||||
Reference in New Issue
Block a user