From b6fbd83d6f7b937d02939089851de91267551011 Mon Sep 17 00:00:00 2001 From: static Date: Thu, 26 Dec 2024 18:54:31 +0900 Subject: [PATCH] =?UTF-8?q?Refresh=20Token=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/hooks/callAPI.ts | 47 +++++++++++++++++++++ src/lib/stores/auth.ts | 3 ++ src/routes/api/auth/login/+server.ts | 12 +++++- src/routes/api/auth/logout/+server.ts | 15 ++----- src/routes/api/auth/refreshToken/+server.ts | 22 +++++----- 5 files changed, 76 insertions(+), 23 deletions(-) create mode 100644 src/lib/hooks/callAPI.ts create mode 100644 src/lib/stores/auth.ts diff --git a/src/lib/hooks/callAPI.ts b/src/lib/hooks/callAPI.ts new file mode 100644 index 0000000..a26ff6f --- /dev/null +++ b/src/lib/hooks/callAPI.ts @@ -0,0 +1,47 @@ +import { accessToken } from "$lib/stores/auth"; + +const refreshToken = async () => { + const res = await fetch("/api/auth/refreshtoken", { + method: "POST", + credentials: "same-origin", + }); + if (!res.ok) { + accessToken.set(null); + throw new Error("Failed to refresh token"); + } + + const data = await res.json(); + const token = data.accessToken as string; + + accessToken.set(token); + return token; +}; + +const callAPIInternal = async ( + input: RequestInfo, + init?: RequestInit, + token?: string | null, + retryIfUnauthorized = true, +): Promise => { + if (token === null) { + token = await refreshToken(); + retryIfUnauthorized = false; + } + + const res = await fetch(input, { + ...init, + headers: { + ...init?.headers, + Authorization: `Bearer ${token}`, + }, + }); + if (res.status === 401 && retryIfUnauthorized) { + return await callAPIInternal(input, init, null, false); + } + + return res; +}; + +export const callAPI = async (input: RequestInfo, init?: RequestInit, token?: string | null) => { + return await callAPIInternal(input, init, token); +}; diff --git a/src/lib/stores/auth.ts b/src/lib/stores/auth.ts new file mode 100644 index 0000000..954cee0 --- /dev/null +++ b/src/lib/stores/auth.ts @@ -0,0 +1,3 @@ +import { writable } from "svelte/store"; + +export const accessToken = writable(null); diff --git a/src/routes/api/auth/login/+server.ts b/src/routes/api/auth/login/+server.ts index 8364a76..dec6e55 100644 --- a/src/routes/api/auth/login/+server.ts +++ b/src/routes/api/auth/login/+server.ts @@ -3,7 +3,7 @@ import { z } from "zod"; import { login } from "$lib/server/services/auth"; import type { RequestHandler } from "./$types"; -export const POST: RequestHandler = async ({ request }) => { +export const POST: RequestHandler = async ({ request, cookies }) => { const zodRes = z .object({ email: z.string().email().nonempty(), @@ -14,5 +14,13 @@ export const POST: RequestHandler = async ({ request }) => { if (!zodRes.success) error(400, zodRes.error.message); const { email, password, pubKey } = zodRes.data; - return json(await login(email.trim(), password.trim(), pubKey?.trim())); + const { accessToken, refreshToken } = await login(email.trim(), password.trim(), pubKey?.trim()); + + cookies.set("refreshToken", refreshToken, { + path: "/api/auth", + httpOnly: true, + secure: true, + sameSite: "strict", + }); + return json({ accessToken }); }; diff --git a/src/routes/api/auth/logout/+server.ts b/src/routes/api/auth/logout/+server.ts index 29df035..0499b87 100644 --- a/src/routes/api/auth/logout/+server.ts +++ b/src/routes/api/auth/logout/+server.ts @@ -1,18 +1,11 @@ import { error, text } from "@sveltejs/kit"; -import { z } from "zod"; import { logout } from "$lib/server/services/auth"; import type { RequestHandler } from "./$types"; -export const POST: RequestHandler = async ({ request }) => { - const zodRes = z - .object({ - refreshToken: z.string().nonempty(), - }) - .safeParse(await request.json()); - if (!zodRes.success) error(400, zodRes.error.message); - - const { refreshToken } = zodRes.data; - await logout(refreshToken.trim()); +export const POST: RequestHandler = async ({ cookies }) => { + const token = cookies.get("refreshToken"); + if (!token) error(401, "Token not found"); + await logout(token.trim()); return text("Logged out"); }; diff --git a/src/routes/api/auth/refreshToken/+server.ts b/src/routes/api/auth/refreshToken/+server.ts index f07de53..62f2a77 100644 --- a/src/routes/api/auth/refreshToken/+server.ts +++ b/src/routes/api/auth/refreshToken/+server.ts @@ -1,16 +1,18 @@ import { error, json } from "@sveltejs/kit"; -import { z } from "zod"; import { refreshToken } from "$lib/server/services/auth"; import type { RequestHandler } from "./$types"; -export const POST: RequestHandler = async ({ request }) => { - const zodRes = z - .object({ - refreshToken: z.string().nonempty(), - }) - .safeParse(await request.json()); - if (!zodRes.success) error(400, zodRes.error.message); +export const POST: RequestHandler = async ({ cookies }) => { + const token = cookies.get("refreshToken"); + if (!token) error(401, "Token not found"); - const { refreshToken: token } = zodRes.data; - return json(await refreshToken(token.trim())); + const { accessToken, refreshToken: newToken } = await refreshToken(token.trim()); + + cookies.set("refreshToken", newToken, { + path: "/api/auth", + httpOnly: true, + secure: true, + sameSite: "strict", + }); + return json({ accessToken }); };