로그인 및 암호 키 등록 이후 클라이언트 승인 대기 페이지로의 자동 리다이렉션 구현

This commit is contained in:
static
2024-12-31 22:32:24 +09:00
parent e5cbd46b35
commit e8e4022bc2
5 changed files with 58 additions and 52 deletions

View File

@@ -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;
};

View File

@@ -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;

View File

@@ -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",

View File

@@ -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;
};

View File

@@ -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;