// src/hooks.server.ts import { optima } from "$lib"; import { redirect, type Handle } from "@sveltejs/kit"; export const handle: Handle = async ({ event, resolve }) => { const accessToken = event.cookies.get("accessToken") || null; const refreshToken = event.cookies.get("refreshToken") || null; if (event.url.pathname === "/logout") { event.cookies.delete("accessToken", { path: "/" }); event.cookies.delete("refreshToken", { path: "/" }); return redirect(303, "/login"); } if (event.url.pathname.startsWith("/login") && optima.user.isLoggedIn()) { return redirect(303, "/"); } if (event.url.pathname.startsWith("/login")) { return await resolve(event); } if (!accessToken && !refreshToken) { optima.user.logout(event); redirect(303, "/login"); } // Check if the access token is expired or near expiry and refresh if needed let currentAccessToken = accessToken; let currentRefreshToken = refreshToken; if (currentAccessToken) { try { const [, payload] = currentAccessToken.split("."); const decoded = JSON.parse( Buffer.from(payload, "base64url").toString("utf8"), ); const nowSec = Math.floor(Date.now() / 1000); const thresholdSec = 60; // refresh if < 60s remaining if (!decoded?.exp || decoded.exp - nowSec < thresholdSec) { // Token is expired or about to expire — try to refresh if (currentRefreshToken) { const refreshed = await optima.user.refreshSession(currentRefreshToken); currentAccessToken = refreshed.accessToken; currentRefreshToken = refreshed.refreshToken ?? currentRefreshToken; } else { // No refresh token available, force re-login optima.user.logout(event); return redirect(303, "/login"); } } } catch { // Token is malformed or refresh failed — try refresh as fallback if (currentRefreshToken) { try { const refreshed = await optima.user.refreshSession(currentRefreshToken); currentAccessToken = refreshed.accessToken; currentRefreshToken = refreshed.refreshToken ?? currentRefreshToken; } catch { // Refresh also failed, force re-login optima.user.logout(event); return redirect(303, "/login"); } } else { optima.user.logout(event); return redirect(303, "/login"); } } } else if (currentRefreshToken) { // No access token but have a refresh token — try to get a new one try { const refreshed = await optima.user.refreshSession(currentRefreshToken); currentAccessToken = refreshed.accessToken; currentRefreshToken = refreshed.refreshToken ?? currentRefreshToken; } catch { optima.user.logout(event); return redirect(303, "/login"); } } const setTokens = async (accessToken: string, refreshToken: string) => { event.cookies.set("accessToken", accessToken, { path: "/" }); event.cookies.set("refreshToken", refreshToken, { path: "/" }); event.locals.session = { accessToken, refreshToken, set: setTokens }; return; }; // Persist any refreshed tokens into cookies await setTokens(currentAccessToken!, currentRefreshToken!); const response = await resolve(event); return response; };