mirror of
https://github.com/kmc7468/arkvault.git
synced 2025-12-15 22:38:47 +00:00
프론트엔드에서 세션 ID 기반 인증 대응 및 DB 마이그레이션 스크립트 재생성
This commit is contained in:
@@ -1,35 +1,11 @@
|
||||
export const refreshToken = async (fetchInternal = fetch) => {
|
||||
return await fetchInternal("/api/auth/refreshToken", { method: "POST" });
|
||||
export const callGetApi = async (input: RequestInfo, fetchInternal = fetch) => {
|
||||
return await fetchInternal(input);
|
||||
};
|
||||
|
||||
const callApi = async (input: RequestInfo, init?: RequestInit, fetchInternal = fetch) => {
|
||||
let res = await fetchInternal(input, init);
|
||||
if (res.status === 401) {
|
||||
res = await refreshToken();
|
||||
if (!res.ok) {
|
||||
return res;
|
||||
}
|
||||
res = await fetchInternal(input, init);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
export const callGetApi = async (input: RequestInfo, fetchInternal?: typeof fetch) => {
|
||||
return await callApi(input, undefined, fetchInternal);
|
||||
};
|
||||
|
||||
export const callPostApi = async <T>(
|
||||
input: RequestInfo,
|
||||
payload?: T,
|
||||
fetchInternal?: typeof fetch,
|
||||
) => {
|
||||
return await callApi(
|
||||
input,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: payload ? JSON.stringify(payload) : undefined,
|
||||
},
|
||||
fetchInternal,
|
||||
);
|
||||
export const callPostApi = async <T>(input: RequestInfo, payload?: T, fetchInternal = fetch) => {
|
||||
return await fetchInternal(input, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: payload ? JSON.stringify(payload) : undefined,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { error, redirect, type Handle } from "@sveltejs/kit";
|
||||
import { authenticate, AuthenticationError } from "$lib/server/modules/auth";
|
||||
|
||||
const whitelist = ["/auth/login", "/api/auth/login"];
|
||||
|
||||
export const authenticateMiddleware: Handle = async ({ event, resolve }) => {
|
||||
const { pathname, search } = event.url;
|
||||
if (whitelist.some((path) => pathname.startsWith(path))) {
|
||||
if (pathname === "/api/auth/login") {
|
||||
return await resolve(event);
|
||||
}
|
||||
|
||||
@@ -19,7 +17,9 @@ export const authenticateMiddleware: Handle = async ({ event, resolve }) => {
|
||||
event.locals.session = await authenticate(sessionIdSigned, ip, userAgent);
|
||||
} catch (e) {
|
||||
if (e instanceof AuthenticationError) {
|
||||
if (pathname.startsWith("/api")) {
|
||||
if (pathname === "/auth/login") {
|
||||
return await resolve(event);
|
||||
} else if (pathname.startsWith("/api")) {
|
||||
error(e.status, e.message);
|
||||
} else {
|
||||
redirect(302, "/auth/login?redirect=" + encodeURIComponent(pathname + search));
|
||||
|
||||
@@ -17,7 +17,7 @@ export const verifyClientEncMekSig = async (
|
||||
) => {
|
||||
const userClient = await getUserClientWithDetails(userId, clientId);
|
||||
if (!userClient) {
|
||||
error(500, "Invalid access token");
|
||||
error(500, "Invalid session id");
|
||||
}
|
||||
|
||||
const data = JSON.stringify({ version, key: encMek });
|
||||
|
||||
@@ -98,7 +98,7 @@ export const verifyUserClient = async (
|
||||
export const getUserClientStatus = async (userId: number, clientId: number) => {
|
||||
const userClient = await getUserClient(userId, clientId);
|
||||
if (!userClient) {
|
||||
error(500, "Invalid access token");
|
||||
error(500, "Invalid session id");
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,37 +1,30 @@
|
||||
import { callPostApi } from "$lib/hooks";
|
||||
import { encodeToBase64, decryptChallenge, signMessage } from "$lib/modules/crypto";
|
||||
import type {
|
||||
TokenUpgradeRequest,
|
||||
TokenUpgradeResponse,
|
||||
TokenUpgradeVerifyRequest,
|
||||
SessionUpgradeRequest,
|
||||
SessionUpgradeResponse,
|
||||
SessionUpgradeVerifyRequest,
|
||||
} from "$lib/server/schemas";
|
||||
|
||||
export const requestTokenUpgrade = async (
|
||||
export const requestSessionUpgrade = async (
|
||||
encryptKeyBase64: string,
|
||||
decryptKey: CryptoKey,
|
||||
verifyKeyBase64: string,
|
||||
signKey: CryptoKey,
|
||||
) => {
|
||||
let res = await fetch("/api/auth/upgradeToken", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
encPubKey: encryptKeyBase64,
|
||||
sigPubKey: verifyKeyBase64,
|
||||
} satisfies TokenUpgradeRequest),
|
||||
let res = await callPostApi<SessionUpgradeRequest>("/api/auth/upgradeSession", {
|
||||
encPubKey: encryptKeyBase64,
|
||||
sigPubKey: verifyKeyBase64,
|
||||
});
|
||||
if (!res.ok) return false;
|
||||
|
||||
const { challenge }: TokenUpgradeResponse = await res.json();
|
||||
const { challenge }: SessionUpgradeResponse = await res.json();
|
||||
const answer = await decryptChallenge(challenge, decryptKey);
|
||||
const answerSig = await signMessage(answer, signKey);
|
||||
|
||||
res = await fetch("/api/auth/upgradeToken/verify", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
answer: encodeToBase64(answer),
|
||||
answerSig: encodeToBase64(answerSig),
|
||||
} satisfies TokenUpgradeVerifyRequest),
|
||||
res = await callPostApi<SessionUpgradeVerifyRequest>("/api/auth/upgradeSession/verify", {
|
||||
answer: encodeToBase64(answer),
|
||||
answerSig: encodeToBase64(answerSig),
|
||||
});
|
||||
return res.ok;
|
||||
};
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { redirect } from "@sveltejs/kit";
|
||||
import type { PageServerLoad } from "./$types";
|
||||
|
||||
export const load: PageServerLoad = async ({ url, cookies }) => {
|
||||
export const load: PageServerLoad = async ({ locals, url }) => {
|
||||
const redirectPath = url.searchParams.get("redirect") || "/home";
|
||||
|
||||
const accessToken = cookies.get("accessToken");
|
||||
if (accessToken) {
|
||||
if (locals.session) {
|
||||
redirect(302, redirectPath);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
import { Button, TextButton } from "$lib/components/buttons";
|
||||
import { TitleDiv, BottomDiv } from "$lib/components/divs";
|
||||
import { TextInput } from "$lib/components/inputs";
|
||||
import { refreshToken } from "$lib/hooks";
|
||||
import { clientKeyStore, masterKeyStore } from "$lib/stores";
|
||||
import { requestLogin, requestTokenUpgrade, requestMasterKeyDownload } from "./service";
|
||||
import { requestLogin, requestSessionUpgrade, requestMasterKeyDownload } from "./service";
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
@@ -25,7 +23,8 @@
|
||||
|
||||
if (!$clientKeyStore) return await redirect("/key/generate");
|
||||
|
||||
if (!(await requestTokenUpgrade($clientKeyStore))) throw new Error("Failed to upgrade token");
|
||||
if (!(await requestSessionUpgrade($clientKeyStore)))
|
||||
throw new Error("Failed to upgrade session");
|
||||
|
||||
// TODO: Multi-user support
|
||||
|
||||
@@ -42,13 +41,6 @@
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
const res = await refreshToken();
|
||||
if (res.ok) {
|
||||
await goto(data.redirectPath, { replaceState: true });
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
import { callPostApi } from "$lib/hooks";
|
||||
import { exportRSAKeyToBase64 } from "$lib/modules/crypto";
|
||||
import type { LoginRequest } from "$lib/server/schemas";
|
||||
import { requestTokenUpgrade as requestTokenUpgradeInternal } from "$lib/services/auth";
|
||||
import { requestSessionUpgrade as requestSessionUpgradeInternal } from "$lib/services/auth";
|
||||
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",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ email, password } satisfies LoginRequest),
|
||||
});
|
||||
const res = await callPostApi<LoginRequest>("/api/auth/login", { email, password });
|
||||
return res.ok;
|
||||
};
|
||||
|
||||
export const requestTokenUpgrade = async ({
|
||||
export const requestSessionUpgrade = async ({
|
||||
encryptKey,
|
||||
decryptKey,
|
||||
signKey,
|
||||
@@ -23,12 +20,12 @@ export const requestTokenUpgrade = async ({
|
||||
}: ClientKeys) => {
|
||||
const encryptKeyBase64 = await exportRSAKeyToBase64(encryptKey);
|
||||
const verifyKeyBase64 = await exportRSAKeyToBase64(verifyKey);
|
||||
if (await requestTokenUpgradeInternal(encryptKeyBase64, decryptKey, verifyKeyBase64, signKey)) {
|
||||
if (await requestSessionUpgradeInternal(encryptKeyBase64, decryptKey, verifyKeyBase64, signKey)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (await requestClientRegistration(encryptKeyBase64, decryptKey, verifyKeyBase64, signKey)) {
|
||||
return await requestTokenUpgradeInternal(
|
||||
return await requestSessionUpgradeInternal(
|
||||
encryptKeyBase64,
|
||||
decryptKey,
|
||||
verifyKeyBase64,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
serializeClientKeys,
|
||||
requestClientRegistration,
|
||||
storeClientKeys,
|
||||
requestTokenUpgrade,
|
||||
requestSessionUpgrade,
|
||||
requestInitialMasterKeyRegistration,
|
||||
} from "./service";
|
||||
|
||||
@@ -59,14 +59,14 @@
|
||||
await storeClientKeys($clientKeyStore);
|
||||
|
||||
if (
|
||||
!(await requestTokenUpgrade(
|
||||
!(await requestSessionUpgrade(
|
||||
data.encryptKeyBase64,
|
||||
$clientKeyStore.decryptKey,
|
||||
data.verifyKeyBase64,
|
||||
$clientKeyStore.signKey,
|
||||
))
|
||||
)
|
||||
throw new Error("Failed to upgrade token");
|
||||
throw new Error("Failed to upgrade session");
|
||||
|
||||
if (
|
||||
!(await requestInitialMasterKeyRegistration(data.masterKeyWrapped, $clientKeyStore.signKey))
|
||||
|
||||
@@ -4,7 +4,7 @@ import { signMasterKeyWrapped } from "$lib/modules/crypto";
|
||||
import type { InitialMasterKeyRegisterRequest } from "$lib/server/schemas";
|
||||
import type { ClientKeys } from "$lib/stores";
|
||||
|
||||
export { requestTokenUpgrade } from "$lib/services/auth";
|
||||
export { requestSessionUpgrade } from "$lib/services/auth";
|
||||
export { requestClientRegistration } from "$lib/services/key";
|
||||
|
||||
type SerializedKeyPairs = {
|
||||
|
||||
Reference in New Issue
Block a user