From 1d0b3102287a6ebe7ef4e15a7fb781a4bbf8e040 Mon Sep 17 00:00:00 2001 From: codey Date: Thu, 28 Aug 2025 11:15:17 -0400 Subject: [PATCH] misc / bugfix: session refresh --- src/components/Footer.astro | 5 - src/components/Login.jsx | 50 ++++++-- src/components/TRip/RequestManagement.jsx | 132 +++++++++++----------- src/hooks/requireAuthHook.js | 91 +++++++-------- src/pages/TRip/index.astro | 24 +--- src/pages/TRip/requests.astro | 6 + src/utils/authFetch.js | 17 +-- 7 files changed, 172 insertions(+), 153 deletions(-) diff --git a/src/components/Footer.astro b/src/components/Footer.astro index 6c5d72d..81b3878 100644 --- a/src/components/Footer.astro +++ b/src/components/Footer.astro @@ -9,11 +9,6 @@ const YEAR = new Date().getFullYear(); --- ); } diff --git a/src/hooks/requireAuthHook.js b/src/hooks/requireAuthHook.js index 443bec8..1c844e2 100644 --- a/src/hooks/requireAuthHook.js +++ b/src/hooks/requireAuthHook.js @@ -1,58 +1,49 @@ -export const requireAuthHook = async () => { - const token = Astro.cookies.get("access_token")?.value; - let user = null; +// requireAuthHook.js +import { API_URL } from "@/config"; +export const requireAuthHook = async (Astro) => { try { - if (!token) throw Error("No access token"); + const cookieHeader = Astro.request.headers.get("cookie") ?? ""; + let res = await fetch(`${API_URL}/auth/id`, { + headers: { Cookie: cookieHeader }, + credentials: "include", + }); - // Step 1: verify current access token - user = verifyToken(token); + if (res.status === 401) { + const refreshRes = await fetch(`${API_URL}/auth/refresh`, { + method: "POST", + headers: { Cookie: cookieHeader }, + credentials: "include", + }); - if (!user) throw Error("Invalid access token"); - - console.log("Verified!", user); - - } catch (err) { - console.log("Access token check failed:", err.message); - - // Step 2: attempt refresh if refresh_token exists - const refreshToken = Astro.cookies.get("refresh_token")?.value; - if (refreshToken) { - try { - const newTokens = await refreshAccessToken(refreshToken); - if (newTokens?.accessToken) { - // store new access token - Astro.cookies.set("access_token", newTokens.accessToken, { - path: "/", - httpOnly: true, - sameSite: "lax", - secure: true, - }); - - // Optionally replace refresh_token too - if (newTokens.refreshToken) { - Astro.cookies.set("refresh_token", newTokens.refreshToken, { - path: "/", - httpOnly: true, - sameSite: "lax", - secure: true, - }); - } - - // re-verify user with new token - user = verifyToken(newTokens.accessToken); - - if (user) { - console.log("Refreshed + verified!", user); - return; // ✅ authenticated now - } - } - } catch (refreshErr) { - console.error("Refresh failed:", refreshErr.message); + if (!refreshRes.ok) { + return null; } + + const setCookieHeader = refreshRes.headers.get("set-cookie"); + let newCookieHeader = cookieHeader; + + if (setCookieHeader) { + const cookiesArray = setCookieHeader.split(/,(?=\s*\w+=)/); + cookiesArray.forEach((c) => Astro.response.headers.append("set-cookie", c)); + + newCookieHeader = cookiesArray.map(c => c.split(";")[0]).join("; "); + } + + res = await fetch(`${API_URL}/auth/id`, { + headers: { Cookie: newCookieHeader }, + credentials: "include", + }); } - // Step 3: if still no user, redirect - return Astro.redirect("/login"); + if (!res.ok) { + return null; + } + const user = await res.json(); + return user; + + } catch (err) { + console.error("[SSR] requireAuthHook error:", err); + return null; } -} \ No newline at end of file +}; diff --git a/src/pages/TRip/index.astro b/src/pages/TRip/index.astro index 56cb474..6b822db 100644 --- a/src/pages/TRip/index.astro +++ b/src/pages/TRip/index.astro @@ -2,29 +2,15 @@ import MediaRequestForm from "@/components/TRip/MediaRequestForm" import Base from "@/layouts/Base.astro"; import Root from "@/components/AppLayout.jsx"; -import { verifyToken } from "@/utils/jwt"; -import { refreshAccessToken } from "@/utils/authFetch"; -import { ENVIRONMENT } from "@/config"; import { requireAuthHook } from "@/hooks/requireAuthHook"; -const token = Astro.cookies.get("access_token")?.value; -let user = null; -try { - if (token) { - user = verifyToken(token); - if (user) { - console.log("Verified!", user); - } else { - throw Error("Authentication required"); - } - } else { - throw Error("Authentication required"); - } -} catch { - return Astro.redirect('/login' - ); +const user = await requireAuthHook(Astro); + +if (!user) { + return Astro.redirect('/login'); } + ---
diff --git a/src/pages/TRip/requests.astro b/src/pages/TRip/requests.astro index 3aaec4d..efdc0cd 100644 --- a/src/pages/TRip/requests.astro +++ b/src/pages/TRip/requests.astro @@ -6,6 +6,12 @@ import { verifyToken } from "@/utils/jwt"; import { requireAuthHook } from "@/hooks/requireAuthHook"; import { ENVIRONMENT } from "@/config"; +const user = await requireAuthHook(Astro); + +if (!user) { + return Astro.redirect('/login'); +} + ---
diff --git a/src/utils/authFetch.js b/src/utils/authFetch.js index 142b3cc..ee5d28e 100644 --- a/src/utils/authFetch.js +++ b/src/utils/authFetch.js @@ -11,7 +11,7 @@ export const authFetch = async (url, options = {}, retry = true) => { if (res.status === 401 && retry) { // attempt refresh try { - const refreshRes = await fetch(`${API_URL}/refresh`, { + const refreshRes = await fetch(`${API_URL}/auth/refresh`, { method: "POST", credentials: "include", }); @@ -30,22 +30,23 @@ export const authFetch = async (url, options = {}, retry = true) => { }; // Refresh token function (HttpOnly cookie flow) -export async function refreshAccessToken() { +export async function refreshAccessToken(cookieHeader) { try { - const res = await fetch(`${API_URL}/refresh`, { + const res = await fetch(`${API_URL}/auth/refresh`, { method: "POST", - credentials: "include", // send HttpOnly cookies + headers: { + cookie: cookieHeader || "", // forward cookies from the request + }, }); if (!res.ok) { throw new Error("Failed to refresh token"); } - // Typically the server just updates the cookie - // It may return a new access token too, but we don’t store it client-side. - return true; + // assume backend responds with new tokens in JSON + return await res.json(); } catch (err) { console.error("Refresh token failed:", err); - return false; + return null; } }