From 8c45131088c882da46a5d223e1e8d7a35cbeb9c9 Mon Sep 17 00:00:00 2001 From: Carlos Monastyrski Date: Wed, 24 Sep 2025 17:55:21 -0300 Subject: [PATCH 1/3] Add frontend interceptor to improve user token expiration and redirect them back to login --- frontend/src/config/request.ts | 64 +++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/frontend/src/config/request.ts b/frontend/src/config/request.ts index 9a0629619..c00261f0c 100644 --- a/frontend/src/config/request.ts +++ b/frontend/src/config/request.ts @@ -1,7 +1,15 @@ import axios from "axios"; +import { addSeconds, formatISO } from "date-fns"; +import { createNotification } from "@app/components/notifications"; import SecurityClient from "@app/components/utilities/SecurityClient"; -import { getAuthToken, getMfaTempToken, getSignupTempToken } from "@app/hooks/api/reactQuery"; +import { SessionStorageKeys } from "@app/const"; +import { + getAuthToken, + getMfaTempToken, + getSignupTempToken, + setAuthToken +} from "@app/hooks/api/reactQuery"; export const apiRequest = axios.create({ baseURL: "/", @@ -34,3 +42,57 @@ apiRequest.interceptors.request.use((config) => { return config; }); + +let isRedirecting = false; + +apiRequest.interceptors.response.use( + (response) => response, + async (error) => { + const { response } = error; + + if (response && (response.status === 401 || response.status === 403)) { + const currentToken = getAuthToken(); + const isAuthenticatedRequest = Boolean(currentToken); + + if (isAuthenticatedRequest && !isRedirecting) { + // Check if the error indicates token expiration + const errorMessage = response.data?.message || ""; + const isTokenExpired = + response.status === 401 || + (errorMessage.toLowerCase().includes("token") && + (errorMessage.toLowerCase().includes("expired") || + errorMessage.toLowerCase().includes("invalid") || + errorMessage.toLowerCase().includes("unauthorized"))); + + if (isTokenExpired) { + isRedirecting = true; + + setAuthToken(""); + SecurityClient.setToken(""); + + createNotification({ + type: "error", + title: "Session Expired", + text: "Your session has expired. Redirecting to login page..." + }); + + sessionStorage.setItem( + SessionStorageKeys.ORG_LOGIN_SUCCESS_REDIRECT_URL, + JSON.stringify({ + expiry: formatISO(addSeconds(new Date(), 300)), // 5 minutes + data: window.location.href + }) + ); + + setTimeout(() => { + window.location.href = "/login"; + }, 5000); // 5 seconds to read the notification + + return Promise.reject(new Error("Session expired - redirecting to login")); + } + } + } + + return Promise.reject(error); + } +); From 297abb4c25b6510460a2c096c48e02362052fb54 Mon Sep 17 00:00:00 2001 From: Carlos Monastyrski Date: Wed, 24 Sep 2025 18:43:20 -0300 Subject: [PATCH 2/3] Address greptile comments --- frontend/src/config/request.ts | 43 ++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/frontend/src/config/request.ts b/frontend/src/config/request.ts index c00261f0c..f9b21c426 100644 --- a/frontend/src/config/request.ts +++ b/frontend/src/config/request.ts @@ -45,6 +45,10 @@ apiRequest.interceptors.request.use((config) => { let isRedirecting = false; +const resetRedirectingFlag = () => { + isRedirecting = false; +}; + apiRequest.interceptors.response.use( (response) => response, async (error) => { @@ -58,17 +62,20 @@ apiRequest.interceptors.response.use( // Check if the error indicates token expiration const errorMessage = response.data?.message || ""; const isTokenExpired = - response.status === 401 || - (errorMessage.toLowerCase().includes("token") && - (errorMessage.toLowerCase().includes("expired") || - errorMessage.toLowerCase().includes("invalid") || - errorMessage.toLowerCase().includes("unauthorized"))); + errorMessage.toLowerCase().includes("token") && + (errorMessage.toLowerCase().includes("expired") || + errorMessage.toLowerCase().includes("invalid") || + errorMessage.toLowerCase().includes("unauthorized")); if (isTokenExpired) { isRedirecting = true; - setAuthToken(""); - SecurityClient.setToken(""); + try { + setAuthToken(""); + SecurityClient.setToken(""); + } catch (err) { + console.warn("Error clearing tokens:", err); + } createNotification({ type: "error", @@ -76,17 +83,23 @@ apiRequest.interceptors.response.use( text: "Your session has expired. Redirecting to login page..." }); - sessionStorage.setItem( - SessionStorageKeys.ORG_LOGIN_SUCCESS_REDIRECT_URL, - JSON.stringify({ - expiry: formatISO(addSeconds(new Date(), 300)), // 5 minutes - data: window.location.href - }) - ); + try { + sessionStorage.setItem( + SessionStorageKeys.ORG_LOGIN_SUCCESS_REDIRECT_URL, + JSON.stringify({ + expiry: formatISO(addSeconds(new Date(), 300)), // 5 minutes + data: window.location.href + }) + ); + } catch (err) { + console.warn("Could not save redirect URL to sessionStorage:", err); + } setTimeout(() => { window.location.href = "/login"; - }, 5000); // 5 seconds to read the notification + }, 2000); // 2 seconds to read the notification + + setTimeout(resetRedirectingFlag, 3000); return Promise.reject(new Error("Session expired - redirecting to login")); } From abf36db47e062202a3f8035bf86689cb7fd8b8ae Mon Sep 17 00:00:00 2001 From: Carlos Monastyrski Date: Wed, 24 Sep 2025 22:17:27 -0300 Subject: [PATCH 3/3] Make isTokenExpired stricter --- frontend/src/config/request.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/frontend/src/config/request.ts b/frontend/src/config/request.ts index f9b21c426..a37b38cb6 100644 --- a/frontend/src/config/request.ts +++ b/frontend/src/config/request.ts @@ -61,11 +61,9 @@ apiRequest.interceptors.response.use( if (isAuthenticatedRequest && !isRedirecting) { // Check if the error indicates token expiration const errorMessage = response.data?.message || ""; - const isTokenExpired = - errorMessage.toLowerCase().includes("token") && - (errorMessage.toLowerCase().includes("expired") || - errorMessage.toLowerCase().includes("invalid") || - errorMessage.toLowerCase().includes("unauthorized")); + const isTokenExpired = errorMessage + .toLowerCase() + .includes("your token has expired. please re-authenticate."); if (isTokenExpired) { isRedirecting = true;