Files
ente/web/packages/base/session.ts
2025-06-05 17:10:11 +05:30

120 lines
3.9 KiB
TypeScript

import { z } from "zod/v4";
import { decryptBoxBytes, toB64 } from "./crypto";
/**
* Remove all data stored in session storage (data tied to the browser tab).
*
* See `docs/storage.md` for more details about session storage. Currently, only
* the following entries are stored in session storage:
*
* - "encryptionKey"
* - "keyEncryptionKey" (transient)
*/
export const clearSessionStorage = () => sessionStorage.clear();
/**
* Schema of JSON string value for the "encryptionKey" and "keyEncryptionKey"
* keys strings stored in session storage.
*/
const SessionKeyData = z.object({
encryptedData: z.string(),
key: z.string(),
nonce: z.string(),
});
/**
* Save the user's encrypted master key in the session storage.
*
* @param keyB64 The user's master key as a base64 encoded string.
*/
// TODO(RE):
// export const saveMasterKeyInSessionStore = async (
// keyB64: string,
// fromDesktop?: boolean,
// ) => {
// const cryptoWorker = await sharedCryptoWorker();
// const sessionKeyAttributes =
// await cryptoWorker.generateKeyAndEncryptToB64(key);
// setKey(keyType, sessionKeyAttributes);
// const electron = globalThis.electron;
// if (electron && !fromDesktop) {
// electron.saveMasterKeyB64(key);
// }
// };
/**
* Return the user's decrypted master key from session storage.
*
* Precondition: The user should be logged in.
*/
export const masterKeyFromSession = async () => {
const key = await masterKeyFromSessionIfLoggedIn();
if (key) {
return key;
} else {
throw new Error(
"The user's master key was not found in session storage. Likely they are not logged in.",
);
}
};
/**
* Return `true` if the user's encrypted master key is present in the session.
*
* Use {@link masterKeyFromSessionIfLoggedIn} to get the actual master key after
* decrypting it. This function is instead useful as a quick check to verify if
* we have credentials at hand or not.
*/
export const haveCredentialsInSession = () =>
!!sessionStorage.getItem("encryptionKey");
/**
* Return the decrypted user's master key from session storage if they are
* logged in, otherwise return `undefined`.
*/
export const masterKeyFromSessionIfLoggedIn = async () => {
// TODO: Same value as the deprecated getKey("encryptionKey")
const value = sessionStorage.getItem("encryptionKey");
if (!value) return undefined;
const { encryptedData, key, nonce } = SessionKeyData.parse(
JSON.parse(value),
);
return decryptBoxBytes({ encryptedData, nonce }, key);
};
/**
* Variant of {@link masterKeyFromSession} that returns the master key as a
* base64 string.
*/
export const masterKeyB64FromSession = () => masterKeyFromSession().then(toB64);
/**
* Return the decrypted user's key encryption key ("kek") from session storage
* if present, otherwise return `undefined`.
*
* [Note: Stashing kek in session store]
*
* During login, if the user has set a second factor (passkey or TOTP), then we
* need to redirect them to the accounts app or TOTP page to verify the second
* factor. This second factor verification happens after password verification,
* but simply storing the decrypted kek in-memory wouldn't work because the
* second factor redirect can happen to a separate accounts app altogether.
*
* So instead, we stash the encrypted kek in session store (using
* {@link stashKeyEncryptionKeyInSessionStore}), and after redirect, retrieve
* it (after clearing it) using {@link unstashKeyEncryptionKeyFromSession}.
*/
export const unstashKeyEncryptionKeyFromSession = async () => {
// TODO: Same value as the deprecated getKey("keyEncryptionKey")
const value = sessionStorage.getItem("keyEncryptionKey");
if (!value) return undefined;
sessionStorage.removeItem("keyEncryptionKey");
const { encryptedData, key, nonce } = SessionKeyData.parse(
JSON.parse(value),
);
return decryptBoxBytes({ encryptedData, nonce }, key);
};