Files
optima/src/hooks.server.ts
T

100 lines
3.3 KiB
TypeScript

// 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;
};