From 516375142d8a58577db0f922f318553174031cba Mon Sep 17 00:00:00 2001 From: static Date: Sun, 29 Dec 2024 01:55:01 +0900 Subject: [PATCH] =?UTF-8?q?=EC=95=94=ED=98=B8=20=ED=82=A4=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=ED=9B=84=20Refresh=20Token=20=EC=97=85=EA=B7=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EB=93=9C=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/modules/auth.ts | 2 +- src/lib/server/services/auth.ts | 25 ++++++++++++++++ .../(fullscreen)/key/export/+page.svelte | 8 ++++- src/routes/(fullscreen)/key/export/service.ts | 11 +++++++ src/routes/api/auth/upgradeToken/+server.ts | 29 +++++++++++++++++++ src/routes/api/key/register/+server.ts | 3 +- src/routes/api/key/verify/+server.ts | 3 +- 7 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 src/routes/api/auth/upgradeToken/+server.ts diff --git a/src/lib/server/modules/auth.ts b/src/lib/server/modules/auth.ts index 32bce3a..8276d6d 100644 --- a/src/lib/server/modules/auth.ts +++ b/src/lib/server/modules/auth.ts @@ -41,7 +41,7 @@ export const authenticate = (cookies: Cookies) => { error(401, "Access token not found"); } - const tokenPayload = verifyToken(accessToken); + const tokenPayload = verifyToken(accessToken.trim()); if (tokenPayload === TokenError.EXPIRED) { error(401, "Access token expired"); } else if (tokenPayload === TokenError.INVALID || tokenPayload.type !== "access") { diff --git a/src/lib/server/services/auth.ts b/src/lib/server/services/auth.ts index e60c4be..ef50978 100644 --- a/src/lib/server/services/auth.ts +++ b/src/lib/server/services/auth.ts @@ -7,6 +7,7 @@ import { getRefreshToken, registerRefreshToken, rotateRefreshToken, + upgradeRefreshToken, revokeRefreshToken, } from "$lib/server/db/token"; import { UserClientState } from "$lib/server/db/schema"; @@ -87,3 +88,27 @@ export const refreshTokens = async (refreshToken: string) => { refreshToken: issueToken({ type: "refresh", jti: newJti }), }; }; + +export const upgradeTokens = async (refreshToken: string, pubKey: string) => { + const { jti: oldJti, userId, clientId } = await verifyRefreshToken(refreshToken); + if (clientId) { + error(403, "Forbidden"); + } + + const client = await getClientByPubKey(pubKey); + const userClient = client ? await getUserClient(userId, client.id) : undefined; + if (!client) { + error(401, "Invalid public key"); + } else if (client && (!userClient || userClient.state === UserClientState.Challenging)) { + error(401, "Unregistered public key"); + } + + const newJti = uuidv4(); + if (!(await upgradeRefreshToken(oldJti, newJti, client.id))) { + error(500, "Refresh token not found"); + } + return { + accessToken: issueAccessToken(userId, client.id), + refreshToken: issueToken({ type: "refresh", jti: newJti }), + }; +}; diff --git a/src/routes/(fullscreen)/key/export/+page.svelte b/src/routes/(fullscreen)/key/export/+page.svelte index 74bc801..762fdc9 100644 --- a/src/routes/(fullscreen)/key/export/+page.svelte +++ b/src/routes/(fullscreen)/key/export/+page.svelte @@ -9,6 +9,7 @@ import { createBlobFromKeyPairBase64, requestPubKeyRegistration, + requestTokenUpgrade, storeKeyPairPersistently, } from "./service"; @@ -40,7 +41,12 @@ if (await requestPubKeyRegistration(data.pubKeyBase64, $keyPairStore.privateKey)) { await storeKeyPairPersistently($keyPairStore); - await goto(data.redirectPath); + + if (await requestTokenUpgrade(data.pubKeyBase64)) { + await goto(data.redirectPath); + } else { + // TODO: Error handling + } } else { // TODO: Error handling } diff --git a/src/routes/(fullscreen)/key/export/service.ts b/src/routes/(fullscreen)/key/export/service.ts index 5ca135e..11b6d53 100644 --- a/src/routes/(fullscreen)/key/export/service.ts +++ b/src/routes/(fullscreen)/key/export/service.ts @@ -38,6 +38,17 @@ export const requestPubKeyRegistration = async (pubKeyBase64: string, privateKey return res.ok; }; +export const requestTokenUpgrade = async (pubKeyBase64: string) => { + const res = await fetch("/api/auth/upgradeToken", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ pubKey: pubKeyBase64 }), + }); + return res.ok; +}; + export const storeKeyPairPersistently = async (keyPair: CryptoKeyPair) => { await storeKeyPairIntoIndexedDB(keyPair.publicKey, keyPair.privateKey); }; diff --git a/src/routes/api/auth/upgradeToken/+server.ts b/src/routes/api/auth/upgradeToken/+server.ts new file mode 100644 index 0000000..20237e4 --- /dev/null +++ b/src/routes/api/auth/upgradeToken/+server.ts @@ -0,0 +1,29 @@ +import { error, text } from "@sveltejs/kit"; +import { z } from "zod"; +import { upgradeTokens } from "$lib/server/services/auth"; +import type { RequestHandler } from "./$types"; + +export const POST: RequestHandler = async ({ request, cookies }) => { + const token = cookies.get("refreshToken"); + if (!token) error(401, "Refresh token not found"); + + const zodRes = z + .object({ + pubKey: z.string().base64().nonempty(), + }) + .safeParse(await request.json()); + if (!zodRes.success) error(400, "Invalid request body"); + + const { pubKey } = zodRes.data; + const { accessToken, refreshToken } = await upgradeTokens(token.trim(), pubKey.trim()); + + cookies.set("accessToken", accessToken, { + path: "/", + sameSite: "strict", + }); + cookies.set("refreshToken", refreshToken, { + path: "/api/auth", + sameSite: "strict", + }); + return text("Token upgraded", { headers: { "Content-Type": "text/plain" } }); +}; diff --git a/src/routes/api/key/register/+server.ts b/src/routes/api/key/register/+server.ts index 766b13f..2be9f3b 100644 --- a/src/routes/api/key/register/+server.ts +++ b/src/routes/api/key/register/+server.ts @@ -17,6 +17,7 @@ export const POST: RequestHandler = async ({ request, cookies, getClientAddress error(403, "Forbidden"); } - const challenge = await registerPubKey(userId, getClientAddress(), zodRes.data.pubKey); + const { pubKey } = zodRes.data; + const challenge = await registerPubKey(userId, getClientAddress(), pubKey.trim()); return json({ challenge }); }; diff --git a/src/routes/api/key/verify/+server.ts b/src/routes/api/key/verify/+server.ts index bc59816..45301e8 100644 --- a/src/routes/api/key/verify/+server.ts +++ b/src/routes/api/key/verify/+server.ts @@ -17,6 +17,7 @@ export const POST: RequestHandler = async ({ request, cookies, getClientAddress error(403, "Forbidden"); } - await verifyPubKey(userId, getClientAddress(), zodRes.data.answer); + const { answer } = zodRes.data; + await verifyPubKey(userId, getClientAddress(), answer.trim()); return text("Key verified", { headers: { "Content-Type": "text/plain" } }); };