From 4fc87b71342aa955616d48c8157cf081ad55374a Mon Sep 17 00:00:00 2001 From: Rafi Arrafif Date: Tue, 17 Feb 2026 21:32:27 +0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=82=20security:=20fix=20auth=20token?= =?UTF-8?q?=20validation=20flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(safe-mode-page)/auth/logout/page.tsx | 9 +++++++++ .../auth/providers/[name]/callback/page.tsx | 0 shared/helpers/backendFetch.ts | 14 +++++--------- shared/models/auth/logout.ts | 6 +++--- shared/models/auth/validateAndDecodeJWT.ts | 17 +++++++---------- 5 files changed, 24 insertions(+), 22 deletions(-) create mode 100644 app/(safe-mode-page)/auth/logout/page.tsx rename app/{(session)/(clean) => (safe-mode-page)}/auth/providers/[name]/callback/page.tsx (100%) diff --git a/app/(safe-mode-page)/auth/logout/page.tsx b/app/(safe-mode-page)/auth/logout/page.tsx new file mode 100644 index 0000000..3d45472 --- /dev/null +++ b/app/(safe-mode-page)/auth/logout/page.tsx @@ -0,0 +1,9 @@ +import { cookies } from "next/headers"; +import { redirect } from "next/navigation"; + +const page = async () => { + (await cookies()).delete("auth_token"); + redirect("/"); +}; + +export default page; diff --git a/app/(session)/(clean)/auth/providers/[name]/callback/page.tsx b/app/(safe-mode-page)/auth/providers/[name]/callback/page.tsx similarity index 100% rename from app/(session)/(clean)/auth/providers/[name]/callback/page.tsx rename to app/(safe-mode-page)/auth/providers/[name]/callback/page.tsx diff --git a/shared/helpers/backendFetch.ts b/shared/helpers/backendFetch.ts index abb0e99..afe346f 100644 --- a/shared/helpers/backendFetch.ts +++ b/shared/helpers/backendFetch.ts @@ -5,6 +5,7 @@ import { UAParser } from "ua-parser-js"; export interface BackendResponse { success: boolean; + status: number; message: string; data?: T; error?: unknown; @@ -34,16 +35,11 @@ export const backendFetch = async (path: string, options: RequestInit = {}) => { ...options.headers, }, cache: "default", - }); + }).then((response) => response.json()); - const resJson = (await res.json()) as BackendResponse; - - if (!res.ok) { - throw new Error(`Elysia error: ${resJson.error}`); - } - - return resJson; - } catch { + return res as BackendResponse; + } catch (res) { + if (process.env.NODE_ENV === "development") return res; redirect("/status?reason=backend-unreachable"); } }; diff --git a/shared/models/auth/logout.ts b/shared/models/auth/logout.ts index 4e01ddb..23ab5bf 100644 --- a/shared/models/auth/logout.ts +++ b/shared/models/auth/logout.ts @@ -1,12 +1,12 @@ "use server"; -import { backendFetch } from "@/shared/helpers/backendFetch"; +import { backendFetch, BackendResponse } from "@/shared/helpers/backendFetch"; import { cookies } from "next/headers"; export const logout = async () => { - const res = await backendFetch("auth/logout", { + const res = (await backendFetch("auth/logout", { method: "POST", - }); + })) as BackendResponse; if (res.success) { (await cookies()).delete("auth_token"); diff --git a/shared/models/auth/validateAndDecodeJWT.ts b/shared/models/auth/validateAndDecodeJWT.ts index ba60b0d..6c2ef10 100644 --- a/shared/models/auth/validateAndDecodeJWT.ts +++ b/shared/models/auth/validateAndDecodeJWT.ts @@ -1,6 +1,7 @@ "use server"; import { backendFetch, BackendResponse } from "@/shared/helpers/backendFetch"; +import { redirect } from "next/navigation"; import { cookies } from "next/headers"; export interface UserSession { @@ -30,18 +31,14 @@ export interface UserSession { } export const validateAndDecodeJWT = async (): Promise => { - const cookieHeader = (await cookies()).get("auth_token")?.value; - - if (!cookieHeader) { - return null; - } - + "use server"; const res = (await backendFetch("auth/token/validate", { method: "POST", - body: JSON.stringify({ - token: cookieHeader, - }), })) as BackendResponse; - return res.data!; + if (res.status === 403) { + redirect("/auth/logout"); + } + + return res.data ?? null; };