mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 16:00:56 +00:00
83 lines
2.4 KiB
TypeScript
83 lines
2.4 KiB
TypeScript
import { isAxiosError } from "axios";
|
|
|
|
import {
|
|
REFRESH_TOKEN_COOKIE,
|
|
clearSessionCookies,
|
|
getCookie,
|
|
} from "./cookies";
|
|
import { refreshSessionTokens } from "./http";
|
|
|
|
/**
|
|
* Proactively refreshes the token pair on a fixed cadence so the server-side
|
|
* session (a sliding 1-hour window, extended only by /auth/refresh-token) is
|
|
* kept alive while the app is open. The 401 interceptor in http.ts remains
|
|
* the reactive fallback; both share the same single-flight refresh call.
|
|
*
|
|
* The interval MUST stay well under the server session window (60 min).
|
|
*/
|
|
const DEFAULT_INTERVAL_MINUTES = 10;
|
|
|
|
const getIntervalMs = () => {
|
|
const minutes = Number(import.meta.env.VITE_TOKEN_REFRESH_INTERVAL_MINUTES);
|
|
return (
|
|
(Number.isFinite(minutes) && minutes > 0
|
|
? minutes
|
|
: DEFAULT_INTERVAL_MINUTES) * 60_000
|
|
);
|
|
};
|
|
|
|
let timerId: number | null = null;
|
|
let lastRefreshAt = 0;
|
|
|
|
const refreshNow = async () => {
|
|
if (!getCookie(REFRESH_TOKEN_COOKIE)) {
|
|
// Logged out elsewhere; nothing to keep alive.
|
|
stopTokenRefreshScheduler();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await refreshSessionTokens();
|
|
lastRefreshAt = Date.now();
|
|
} catch (error) {
|
|
// Network hiccups are retried on the next tick; only an explicit server
|
|
// rejection means the session is dead.
|
|
if (isAxiosError(error) && error.response) {
|
|
stopTokenRefreshScheduler();
|
|
clearSessionCookies();
|
|
window.location.replace("/auth");
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Browsers freeze timers in background tabs — a tab waking up past its
|
|
* refresh deadline refreshes immediately instead of waiting a full interval.
|
|
*/
|
|
const onVisibilityChange = () => {
|
|
if (document.visibilityState !== "visible") return;
|
|
if (Date.now() - lastRefreshAt >= getIntervalMs()) {
|
|
void refreshNow();
|
|
}
|
|
};
|
|
|
|
export const startTokenRefreshScheduler = () => {
|
|
stopTokenRefreshScheduler();
|
|
|
|
// Token age is unknown here (fresh login vs. hours-old page reload), so
|
|
// refresh right away to extend the session window from "now".
|
|
lastRefreshAt = 0;
|
|
void refreshNow();
|
|
|
|
timerId = window.setInterval(() => void refreshNow(), getIntervalMs());
|
|
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
};
|
|
|
|
export const stopTokenRefreshScheduler = () => {
|
|
if (timerId !== null) {
|
|
window.clearInterval(timerId);
|
|
timerId = null;
|
|
}
|
|
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
};
|