From c00dbe70248f02546bb7109c87ac5ad5670afc95 Mon Sep 17 00:00:00 2001 From: static Date: Sat, 28 Dec 2024 01:05:31 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B3=B5=EA=B0=9C=20=ED=82=A4=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/server/db/client.ts | 8 +++++++ src/lib/server/modules/auth.ts | 20 +++++++++++++++++ src/lib/server/services/key.ts | 10 +++++++++ src/routes/(fullscreen)/auth/login/service.ts | 3 +-- .../(fullscreen)/key/export/+page.svelte | 9 +++++++- src/routes/(fullscreen)/key/export/service.ts | 12 ++++++++++ src/routes/api/auth/login/+server.ts | 2 +- src/routes/api/key/register/+server.ts | 22 +++++++++++++++++++ 8 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 src/lib/server/services/key.ts create mode 100644 src/routes/(fullscreen)/key/export/service.ts create mode 100644 src/routes/api/key/register/+server.ts diff --git a/src/lib/server/db/client.ts b/src/lib/server/db/client.ts index a4e2cdb..c3c64ad 100644 --- a/src/lib/server/db/client.ts +++ b/src/lib/server/db/client.ts @@ -2,6 +2,14 @@ import { and, eq } from "drizzle-orm"; import db from "./drizzle"; import { client, userClient } from "./schema"; +export const createClient = async (pubKey: string, userId: number) => { + await db.transaction(async (tx) => { + const insertRes = await tx.insert(client).values({ pubKey }).returning({ id: client.id }); + const { id: clientId } = insertRes[0]!; + await tx.insert(userClient).values({ userId, clientId }); + }); +}; + export const getClientByPubKey = async (pubKey: string) => { const clients = await db.select().from(client).where(eq(client.pubKey, pubKey)).execute(); return clients[0] ?? null; diff --git a/src/lib/server/modules/auth.ts b/src/lib/server/modules/auth.ts index 9ab7420..4d456b1 100644 --- a/src/lib/server/modules/auth.ts +++ b/src/lib/server/modules/auth.ts @@ -1,3 +1,4 @@ +import { error } from "@sveltejs/kit"; import jwt from "jsonwebtoken"; import env from "$lib/server/loadenv"; @@ -36,3 +37,22 @@ export const verifyToken = (token: string) => { return TokenError.INVALID; } }; + +export const authenticate = (request: Request) => { + const accessToken = request.headers.get("Authorization"); + if (!accessToken?.startsWith("Bearer ")) { + error(401, "Token required"); + } + + const tokenData = verifyToken(accessToken.slice(7)); + if (tokenData === TokenError.EXPIRED) { + error(401, "Token expired"); + } else if (tokenData === TokenError.INVALID || tokenData.type !== "access") { + error(401, "Invalid token"); + } + + return { + userId: tokenData.userId, + clientId: tokenData.clientId, + }; +}; diff --git a/src/lib/server/services/key.ts b/src/lib/server/services/key.ts new file mode 100644 index 0000000..4d57e08 --- /dev/null +++ b/src/lib/server/services/key.ts @@ -0,0 +1,10 @@ +import { error } from "@sveltejs/kit"; +import { createClient, getClientByPubKey } from "$lib/server/db/client"; + +export const registerPubKey = async (userId: number, pubKey: string) => { + if (await getClientByPubKey(pubKey)) { + error(409, "Public key already registered"); + } + + await createClient(pubKey, userId); +}; diff --git a/src/routes/(fullscreen)/auth/login/service.ts b/src/routes/(fullscreen)/auth/login/service.ts index f88dbc8..47f8f5b 100644 --- a/src/routes/(fullscreen)/auth/login/service.ts +++ b/src/routes/(fullscreen)/auth/login/service.ts @@ -1,8 +1,7 @@ -import { callAPI } from "$lib/hooks"; import { accessTokenStore } from "$lib/stores"; export const requestLogin = async (email: string, password: string) => { - const res = await callAPI("/api/auth/login", { + const res = await fetch("/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/src/routes/(fullscreen)/key/export/+page.svelte b/src/routes/(fullscreen)/key/export/+page.svelte index 14d2656..73fa06a 100644 --- a/src/routes/(fullscreen)/key/export/+page.svelte +++ b/src/routes/(fullscreen)/key/export/+page.svelte @@ -2,6 +2,7 @@ import { Button, TextButton } from "$lib/components/buttons"; import { BottomDiv } from "$lib/components/divs"; import BeforeContinueModal from "./BeforeContinueModal.svelte"; + import { requestPubKeyRegistration } from "./service"; import IconKey from "~icons/material-symbols/key"; @@ -15,9 +16,15 @@ console.log(data.privKeyBase64); }; - const continueWithoutExport = () => { + const continueWithoutExport = async () => { isBeforeContinueModalOpen = false; + const ok = await requestPubKeyRegistration(data.pubKeyBase64); + if (!ok) { + // TODO + return; + } + // TODO }; diff --git a/src/routes/(fullscreen)/key/export/service.ts b/src/routes/(fullscreen)/key/export/service.ts new file mode 100644 index 0000000..3cef2bc --- /dev/null +++ b/src/routes/(fullscreen)/key/export/service.ts @@ -0,0 +1,12 @@ +import { callAPI } from "$lib/hooks"; + +export const requestPubKeyRegistration = async (pubKeyBase64: string) => { + const res = await callAPI("/api/key/register", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ pubKey: pubKeyBase64 }), + }); + return res.ok; +}; diff --git a/src/routes/api/auth/login/+server.ts b/src/routes/api/auth/login/+server.ts index 65f7623..7a4351b 100644 --- a/src/routes/api/auth/login/+server.ts +++ b/src/routes/api/auth/login/+server.ts @@ -13,7 +13,7 @@ export const POST: RequestHandler = async ({ request, cookies }) => { pubKey: z.string().nonempty().optional(), }) .safeParse(await request.json()); - if (!zodRes.success) error(400, zodRes.error.message); + if (!zodRes.success) error(400, "Invalid request body"); const { email, password, pubKey } = zodRes.data; const { accessToken, refreshToken } = await login(email.trim(), password.trim(), pubKey?.trim()); diff --git a/src/routes/api/key/register/+server.ts b/src/routes/api/key/register/+server.ts new file mode 100644 index 0000000..1c98bb4 --- /dev/null +++ b/src/routes/api/key/register/+server.ts @@ -0,0 +1,22 @@ +import { error, text } from "@sveltejs/kit"; +import { z } from "zod"; +import { authenticate } from "$lib/server/modules/auth"; +import { registerPubKey } from "$lib/server/services/key"; +import type { RequestHandler } from "./$types"; + +export const POST: RequestHandler = async ({ request }) => { + const zodRes = z + .object({ + pubKey: z.string().base64().nonempty(), + }) + .safeParse(await request.json()); + if (!zodRes.success) error(400, "Invalid request body"); + + const { userId, clientId } = authenticate(request); + if (clientId) { + error(403, "Forbidden"); + } + + await registerPubKey(userId, zodRes.data.pubKey); + return text("Public key registered"); +};