diff --git a/src/lib/services/key.ts b/src/lib/services/key.ts index 4d7b97a..d55cb93 100644 --- a/src/lib/services/key.ts +++ b/src/lib/services/key.ts @@ -1,10 +1,14 @@ import { callAPI } from "$lib/hooks"; +import { storeMasterKeys } from "$lib/indexedDB"; import { encodeToBase64, decodeFromBase64, decryptRSACiphertext, signRSAMessage, + makeAESKeyNonextractable, + unwrapAESKeyUsingRSA, } from "$lib/modules/crypto"; +import { masterKeyStore } from "$lib/stores"; export const requestClientRegistration = async ( encryptKeyBase64: string, @@ -40,3 +44,35 @@ export const requestClientRegistration = async ( }); return res.ok; }; + +export const requestMasterKeyDownload = async (decryptKey: CryptoKey) => { + const res = await callAPI("/api/mek/list", { method: "GET" }); + if (!res.ok) return false; + + const data = await res.json(); + const { meks: masterKeysWrapped } = data as { + meks: { + version: number; + state: "active" | "retired"; + mek: string; + }[]; + }; + const masterKeys = await Promise.all( + masterKeysWrapped.map(async ({ version, state, mek: masterKeyWrapped }) => ({ + version, + state, + masterKey: await makeAESKeyNonextractable( + await unwrapAESKeyUsingRSA(decodeFromBase64(masterKeyWrapped), decryptKey), + ), + })), + ); + + await storeMasterKeys( + masterKeys.map(({ version, state, masterKey }) => ({ version, state, key: masterKey })), + ); + masterKeyStore.set( + new Map(masterKeys.map(({ version, state, masterKey }) => [version, { state, masterKey }])), + ); + + return true; +}; diff --git a/src/routes/(fullscreen)/auth/login/+page.svelte b/src/routes/(fullscreen)/auth/login/+page.svelte index 556911f..776bb6d 100644 --- a/src/routes/(fullscreen)/auth/login/+page.svelte +++ b/src/routes/(fullscreen)/auth/login/+page.svelte @@ -5,28 +5,35 @@ import { TitleDiv, BottomDiv } from "$lib/components/divs"; import { TextInput } from "$lib/components/inputs"; import { refreshToken } from "$lib/hooks/callAPI"; - import { clientKeyStore } from "$lib/stores"; - import { requestLogin, requestTokenUpgrade } from "./service"; + import { clientKeyStore, masterKeyStore } from "$lib/stores"; + import { requestLogin, requestTokenUpgrade, requestMasterKeyDownload } from "./service"; let { data } = $props(); let email = $state(""); let password = $state(""); + const redirect = async (url: string) => { + return await goto(`${url}?redirect=${encodeURIComponent(data.redirectPath)}`); + }; + const login = async () => { // TODO: Validation try { if (!(await requestLogin(email, password))) throw new Error("Failed to login"); - if ($clientKeyStore && !(await requestTokenUpgrade($clientKeyStore))) - throw new Error("Failed to upgrade token"); + if (!$clientKeyStore) return await redirect("/key/generate"); - await goto( - $clientKeyStore - ? data.redirectPath - : "/key/generate?redirect=" + encodeURIComponent(data.redirectPath), - ); + if (!(await requestTokenUpgrade($clientKeyStore))) throw new Error("Failed to upgrade token"); + + // TODO: Multi-user support + + if ($masterKeyStore || (await requestMasterKeyDownload($clientKeyStore.decryptKey))) { + await goto(data.redirectPath); + } else { + await redirect("/client/pending"); + } } catch (e) { // TODO: Alert throw e; diff --git a/src/routes/(fullscreen)/auth/login/service.ts b/src/routes/(fullscreen)/auth/login/service.ts index 77c4620..e9002af 100644 --- a/src/routes/(fullscreen)/auth/login/service.ts +++ b/src/routes/(fullscreen)/auth/login/service.ts @@ -3,6 +3,8 @@ import { requestTokenUpgrade as requestTokenUpgradeInternal } from "$lib/service import { requestClientRegistration } from "$lib/services/key"; import type { ClientKeys } from "$lib/stores"; +export { requestMasterKeyDownload } from "$lib/services/key"; + export const requestLogin = async (email: string, password: string) => { const res = await fetch("/api/auth/login", { method: "POST", diff --git a/src/routes/(fullscreen)/client/pending/service.ts b/src/routes/(fullscreen)/client/pending/service.ts index 51037b5..5a1a06d 100644 --- a/src/routes/(fullscreen)/client/pending/service.ts +++ b/src/routes/(fullscreen)/client/pending/service.ts @@ -1,13 +1,6 @@ -import { callAPI } from "$lib/hooks"; -import { storeMasterKeys } from "$lib/indexedDB"; -import { - decodeFromBase64, - exportRSAKey, - makeAESKeyNonextractable, - unwrapAESKeyUsingRSA, - digestSHA256, -} from "$lib/modules/crypto"; -import { masterKeyStore } from "$lib/stores"; +import { exportRSAKey, digestSHA256 } from "$lib/modules/crypto"; + +export { requestMasterKeyDownload } from "$lib/services/key"; export const generateEncryptKeyFingerprint = async (encryptKey: CryptoKey) => { const { key } = await exportRSAKey(encryptKey); @@ -19,35 +12,3 @@ export const generateEncryptKeyFingerprint = async (encryptKey: CryptoKey) => { .match(/.{1,4}/g)! .join(" "); }; - -export const requestMasterKeyDownload = async (decryptKey: CryptoKey) => { - const res = await callAPI("/api/mek/list", { method: "GET" }); - if (!res.ok) return false; - - const data = await res.json(); - const { meks: masterKeysWrapped } = data as { - meks: { - version: number; - state: "active" | "retired"; - mek: string; - }[]; - }; - const masterKeys = await Promise.all( - masterKeysWrapped.map(async ({ version, state, mek: masterKeyWrapped }) => ({ - version, - state, - masterKey: await makeAESKeyNonextractable( - await unwrapAESKeyUsingRSA(decodeFromBase64(masterKeyWrapped), decryptKey), - ), - })), - ); - - await storeMasterKeys( - masterKeys.map(({ version, state, masterKey }) => ({ version, state, key: masterKey })), - ); - masterKeyStore.set( - new Map(masterKeys.map(({ version, state, masterKey }) => [version, { state, masterKey }])), - ); - - return true; -}; diff --git a/src/routes/(fullscreen)/key/export/+page.svelte b/src/routes/(fullscreen)/key/export/+page.svelte index 31d7384..b45747c 100644 --- a/src/routes/(fullscreen)/key/export/+page.svelte +++ b/src/routes/(fullscreen)/key/export/+page.svelte @@ -76,7 +76,7 @@ ) throw new Error("Failed to register initial MEK"); - await goto(data.redirectPath); + await goto("/client/pending?redirect=" + encodeURIComponent(data.redirectPath)); } catch (e) { // TODO: Error handling throw e;