fix: persist session password in sessionStorage to survive page refreshes

This commit is contained in:
Roland Tannous 2026-04-01 19:19:18 +00:00
commit 3cddb8f953
2 changed files with 36 additions and 9 deletions

View file

@ -16,23 +16,49 @@ const PBKDF2_ITERATIONS = 100_000;
const SALT_BYTES = 16;
const IV_BYTES = 12;
// ── Session password (in-memory only) ────────────────────────────
// ── Session password ─────────────────────────────────────────────
//
// Held in a module variable for fast access, backed by sessionStorage
// so it survives page refreshes within the same tab. Cleared on
// logout and when the tab is closed (sessionStorage semantics).
const SESSION_PW_KEY = "unsloth_chat_session_pw";
let _sessionPassword: string | null = null;
/** Store the login password in memory for the duration of the session. */
/** Store the login password for the duration of the browser session. */
export function setSessionPassword(password: string): void {
_sessionPassword = password;
try {
sessionStorage.setItem(SESSION_PW_KEY, password);
} catch {
// Private browsing or quota — in-memory only
}
}
/** Retrieve the in-memory session password. Returns null when logged out. */
/** Retrieve the session password. Restores from sessionStorage after a page refresh. */
export function getSessionPassword(): string | null {
return _sessionPassword;
if (_sessionPassword) return _sessionPassword;
try {
const stored = sessionStorage.getItem(SESSION_PW_KEY);
if (stored) {
_sessionPassword = stored;
return stored;
}
} catch {
// ignore
}
return null;
}
/** Clear the in-memory session password (called on logout). */
/** Clear the session password (called on logout). */
export function clearSessionPassword(): void {
_sessionPassword = null;
try {
sessionStorage.removeItem(SESSION_PW_KEY);
} catch {
// ignore
}
}
// ── Key derivation ───────────────────────────────────────────────

View file

@ -226,7 +226,7 @@ export async function getExternalProviderApiKey(
/**
* Store a provider API key, encrypting it before writing to localStorage.
* Falls back to plaintext storage if no session password is available.
* Throws if no session password is available (should not happen in normal flow).
*/
export async function setExternalProviderApiKey(
providerId: string,
@ -236,9 +236,10 @@ export async function setExternalProviderApiKey(
try {
const keys = loadRawKeyMap();
const password = getSessionPassword();
keys[providerId] = password
? await encryptValue(apiKey, password)
: apiKey;
if (!password) {
throw new Error("No session password — cannot encrypt API key");
}
keys[providerId] = await encryptValue(apiKey, password);
saveRawKeyMap(keys);
} catch {
// ignore