// requireAuthHook.js import { API_URL } from "@/config"; // WeakMap to cache auth promises per Astro context (request) const authCache = new WeakMap(); export const requireAuthHook = async (Astro) => { // Check if we already have a cached promise for this request if (authCache.has(Astro)) { return authCache.get(Astro); } // Create a promise and cache it immediately to prevent race conditions const authPromise = performAuth(Astro); authCache.set(Astro, authPromise); // Return the promise - all callers will await the same promise return authPromise; }; async function performAuth(Astro) { try { const cookieHeader = Astro.request.headers.get("cookie") ?? ""; let res = await fetch(`${API_URL}/auth/id`, { headers: { Cookie: cookieHeader }, credentials: "include", }); if (res.status === 401) { const refreshRes = await fetch(`${API_URL}/auth/refresh`, { method: "POST", headers: { Cookie: cookieHeader }, credentials: "include", }); if (!refreshRes.ok) { console.error("Token refresh failed", refreshRes.status); return null; } // Get all Set-Cookie headers (getSetCookie returns an array) let setCookies = []; if (typeof refreshRes.headers.getSetCookie === 'function') { setCookies = refreshRes.headers.getSetCookie(); } else { // Fallback for older Node versions const setCookieHeader = refreshRes.headers.get("set-cookie"); if (setCookieHeader) { // Split on comma followed by a cookie name (word=), avoiding splitting on Expires dates setCookies = setCookieHeader.split(/,(?=\s*[a-zA-Z_][a-zA-Z0-9_]*=)/); } } if (setCookies.length === 0) { console.error("No set-cookie header found in refresh response"); return null; } // Forward cookies to client setCookies.forEach((c) => Astro.response.headers.append("set-cookie", c.trim())); // Build new cookie header for the retry request const newCookieHeader = setCookies.map(c => c.split(";")[0].trim()).join("; "); res = await fetch(`${API_URL}/auth/id`, { headers: { Cookie: newCookieHeader }, credentials: "include", }); } if (!res.ok) { console.error("Failed to fetch user ID after token refresh", res.status); return null; } const user = await res.json(); return user; } catch (err) { console.error("[SSR] requireAuthHook error:", err); return null; } }