mirror of
https://github.com/kmc7468/arkvault.git
synced 2025-12-16 15:08:46 +00:00
강제 로그인 기능 추가
This commit is contained in:
@@ -3,10 +3,18 @@
|
||||
import { BottomDiv, Button, FullscreenDiv, TextButton, TextInput } from "$lib/components/atoms";
|
||||
import { TitledDiv } from "$lib/components/molecules";
|
||||
import { clientKeyStore, masterKeyStore } from "$lib/stores";
|
||||
import { requestLogin, requestSessionUpgrade, requestMasterKeyDownload } from "./service";
|
||||
import ForceLoginModal from "./ForceLoginModal.svelte";
|
||||
import {
|
||||
requestLogout,
|
||||
requestLogin,
|
||||
requestSessionUpgrade,
|
||||
requestMasterKeyDownload,
|
||||
} from "./service";
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
let isForceLoginModalOpen = $state(false);
|
||||
|
||||
let email = $state("");
|
||||
let password = $state("");
|
||||
|
||||
@@ -14,6 +22,32 @@
|
||||
return await goto(`${url}?redirect=${encodeURIComponent(data.redirectPath)}`);
|
||||
};
|
||||
|
||||
const upgradeSession = async (force: boolean) => {
|
||||
try {
|
||||
const [upgradeRes, upgradeError] = await requestSessionUpgrade($clientKeyStore!, force);
|
||||
if (!force && upgradeError === "Already logged in") {
|
||||
isForceLoginModalOpen = true;
|
||||
return;
|
||||
} else if (!upgradeRes) {
|
||||
throw new Error("Failed to upgrade session");
|
||||
}
|
||||
|
||||
// TODO: Multi-user support
|
||||
|
||||
if (
|
||||
$masterKeyStore ||
|
||||
(await requestMasterKeyDownload($clientKeyStore!.decryptKey, $clientKeyStore!.verifyKey))
|
||||
) {
|
||||
await goto(data.redirectPath);
|
||||
} else {
|
||||
await redirect("/client/pending");
|
||||
}
|
||||
} catch (e) {
|
||||
// TODO
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
const login = async () => {
|
||||
// TODO: Validation
|
||||
|
||||
@@ -22,19 +56,7 @@
|
||||
|
||||
if (!$clientKeyStore) return await redirect("/key/generate");
|
||||
|
||||
if (!(await requestSessionUpgrade($clientKeyStore)))
|
||||
throw new Error("Failed to upgrade session");
|
||||
|
||||
// TODO: Multi-user support
|
||||
|
||||
if (
|
||||
$masterKeyStore ||
|
||||
(await requestMasterKeyDownload($clientKeyStore.decryptKey, $clientKeyStore.verifyKey))
|
||||
) {
|
||||
await goto(data.redirectPath);
|
||||
} else {
|
||||
await redirect("/client/pending");
|
||||
}
|
||||
await upgradeSession(false);
|
||||
} catch (e) {
|
||||
// TODO: Alert
|
||||
throw e;
|
||||
@@ -63,3 +85,9 @@
|
||||
<TextButton>계정이 없어요</TextButton>
|
||||
</BottomDiv>
|
||||
</FullscreenDiv>
|
||||
|
||||
<ForceLoginModal
|
||||
bind:isOpen={isForceLoginModalOpen}
|
||||
oncancel={requestLogout}
|
||||
onLoginClick={() => upgradeSession(true)}
|
||||
/>
|
||||
|
||||
22
src/routes/(fullscreen)/auth/login/ForceLoginModal.svelte
Normal file
22
src/routes/(fullscreen)/auth/login/ForceLoginModal.svelte
Normal file
@@ -0,0 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { ActionModal } from "$lib/components/molecules";
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
oncancel: () => void;
|
||||
onLoginClick: () => void;
|
||||
}
|
||||
|
||||
let { isOpen = $bindable(), oncancel, onLoginClick }: Props = $props();
|
||||
</script>
|
||||
|
||||
<ActionModal
|
||||
bind:isOpen
|
||||
title="다른 디바이스에 이미 로그인되어 있어요."
|
||||
cancelText="아니요"
|
||||
{oncancel}
|
||||
confirmText="네"
|
||||
onConfirmClick={onLoginClick}
|
||||
>
|
||||
<p>다른 디바이스에서는 로그아웃하고, 이 디바이스에서 로그인할까요?</p>
|
||||
</ActionModal>
|
||||
@@ -5,6 +5,7 @@ import { requestSessionUpgrade as requestSessionUpgradeInternal } from "$lib/ser
|
||||
import { requestClientRegistration } from "$lib/services/key";
|
||||
import type { ClientKeys } from "$lib/stores";
|
||||
|
||||
export { requestLogout } from "$lib/services/auth";
|
||||
export { requestMasterKeyDownload } from "$lib/services/key";
|
||||
|
||||
export const requestLogin = async (email: string, password: string) => {
|
||||
@@ -12,26 +13,33 @@ export const requestLogin = async (email: string, password: string) => {
|
||||
return res.ok;
|
||||
};
|
||||
|
||||
export const requestSessionUpgrade = async ({
|
||||
encryptKey,
|
||||
decryptKey,
|
||||
signKey,
|
||||
verifyKey,
|
||||
}: ClientKeys) => {
|
||||
export const requestSessionUpgrade = async (
|
||||
{ encryptKey, decryptKey, signKey, verifyKey }: ClientKeys,
|
||||
force: boolean,
|
||||
) => {
|
||||
const encryptKeyBase64 = await exportRSAKeyToBase64(encryptKey);
|
||||
const verifyKeyBase64 = await exportRSAKeyToBase64(verifyKey);
|
||||
if (await requestSessionUpgradeInternal(encryptKeyBase64, decryptKey, verifyKeyBase64, signKey)) {
|
||||
return true;
|
||||
const [res, error] = await requestSessionUpgradeInternal(
|
||||
encryptKeyBase64,
|
||||
decryptKey,
|
||||
verifyKeyBase64,
|
||||
signKey,
|
||||
force,
|
||||
);
|
||||
if (error === undefined) return [res] as const;
|
||||
|
||||
if (
|
||||
error === "Unregistered client" &&
|
||||
!(await requestClientRegistration(encryptKeyBase64, decryptKey, verifyKeyBase64, signKey))
|
||||
) {
|
||||
return [false] as const;
|
||||
} else if (error === "Already logged in") {
|
||||
return [false, force ? undefined : error] as const;
|
||||
}
|
||||
|
||||
if (await requestClientRegistration(encryptKeyBase64, decryptKey, verifyKeyBase64, signKey)) {
|
||||
return await requestSessionUpgradeInternal(
|
||||
encryptKeyBase64,
|
||||
decryptKey,
|
||||
verifyKeyBase64,
|
||||
signKey,
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return [
|
||||
(
|
||||
await requestSessionUpgradeInternal(encryptKeyBase64, decryptKey, verifyKeyBase64, signKey)
|
||||
)[0],
|
||||
] as const;
|
||||
};
|
||||
|
||||
@@ -59,12 +59,14 @@
|
||||
await storeClientKeys($clientKeyStore);
|
||||
|
||||
if (
|
||||
!(await requestSessionUpgrade(
|
||||
data.encryptKeyBase64,
|
||||
$clientKeyStore.decryptKey,
|
||||
data.verifyKeyBase64,
|
||||
$clientKeyStore.signKey,
|
||||
))
|
||||
!(
|
||||
await requestSessionUpgrade(
|
||||
data.encryptKeyBase64,
|
||||
$clientKeyStore.decryptKey,
|
||||
data.verifyKeyBase64,
|
||||
$clientKeyStore.signKey,
|
||||
)
|
||||
)[0]
|
||||
)
|
||||
throw new Error("Failed to upgrade session");
|
||||
|
||||
|
||||
@@ -1,6 +1 @@
|
||||
import { callPostApi } from "$lib/hooks";
|
||||
|
||||
export const requestLogout = async () => {
|
||||
const res = await callPostApi("/api/auth/logout");
|
||||
return res.ok;
|
||||
};
|
||||
export { requestLogout } from "$lib/services/auth";
|
||||
|
||||
@@ -5,12 +5,12 @@ import { verifySessionUpgradeChallenge } from "$lib/server/services/auth";
|
||||
import type { RequestHandler } from "./$types";
|
||||
|
||||
export const POST: RequestHandler = async ({ locals, request }) => {
|
||||
const { sessionId } = await authorize(locals, "notClient");
|
||||
const { sessionId, userId } = await authorize(locals, "notClient");
|
||||
|
||||
const zodRes = sessionUpgradeVerifyRequest.safeParse(await request.json());
|
||||
if (!zodRes.success) error(400, "Invalid request body");
|
||||
const { id, answerSig } = zodRes.data;
|
||||
const { id, answerSig, force } = zodRes.data;
|
||||
|
||||
await verifySessionUpgradeChallenge(sessionId, locals.ip, id, answerSig);
|
||||
await verifySessionUpgradeChallenge(sessionId, userId, locals.ip, id, answerSig, force);
|
||||
return text("Session upgraded", { headers: { "Content-Type": "text/plain" } });
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user