From 9811757f090433126888f91e014d71240db72dd7 Mon Sep 17 00:00:00 2001 From: ibolton336 Date: Mon, 11 Dec 2023 19:48:10 -0500 Subject: [PATCH] :bug: Fix session expiry when refresh tokens are disabled Signed-off-by: ibolton336 --- client/src/app/axios-config/apiInit.ts | 39 +++++++- .../src/app/components/KeycloakProvider.tsx | 98 +++---------------- client/src/app/layout/HeaderApp/SSOMenu.tsx | 5 +- 3 files changed, 51 insertions(+), 91 deletions(-) diff --git a/client/src/app/axios-config/apiInit.ts b/client/src/app/axios-config/apiInit.ts index 06e3c97e4e..913ab563df 100644 --- a/client/src/app/axios-config/apiInit.ts +++ b/client/src/app/axios-config/apiInit.ts @@ -1,14 +1,43 @@ import axios from "axios"; +import keycloak from "@app/keycloak"; -export const initInterceptors = (getToken: () => Promise) => { +export const initInterceptors = () => { axios.interceptors.request.use( - async (config) => { - const token = await getToken(); - if (token) config.headers["Authorization"] = "Bearer " + token; + (config) => { + const token = keycloak.token; + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } return config; }, (error) => { - Promise.reject(error); + return Promise.reject(error); + } + ); + + axios.interceptors.response.use( + (response) => { + return response; + }, + async (error) => { + if (error.response && error.response.status === 401) { + try { + const refreshed = await keycloak.updateToken(5); + if (refreshed) { + const retryConfig = { + ...error.config, + headers: { + ...error.config.headers, + Authorization: `Bearer ${keycloak.token}`, + }, + }; + return axios(retryConfig); + } + } catch (refreshError) { + keycloak.login(); + } + } + return Promise.reject(error); } ); }; diff --git a/client/src/app/components/KeycloakProvider.tsx b/client/src/app/components/KeycloakProvider.tsx index daf2ed50f5..363be14fed 100644 --- a/client/src/app/components/KeycloakProvider.tsx +++ b/client/src/app/components/KeycloakProvider.tsx @@ -1,12 +1,8 @@ -import { initInterceptors } from "@app/axios-config"; -import { isAuthRequired } from "@app/Constants"; -import i18n from "@app/i18n"; +import React, { Suspense } from "react"; +import { ReactKeycloakProvider } from "@react-keycloak/web"; import keycloak from "@app/keycloak"; -import { deleteCookie, getCookie, setCookie } from "@app/queries/cookies"; import { AppPlaceholder } from "./AppPlaceholder"; -import { Flex, FlexItem, Spinner } from "@patternfly/react-core"; -import { ReactKeycloakProvider } from "@react-keycloak/web"; -import React, { Suspense } from "react"; +import { initInterceptors } from "@app/axios-config"; interface IKeycloakProviderProps { children: React.ReactNode; @@ -15,81 +11,17 @@ interface IKeycloakProviderProps { export const KeycloakProvider: React.FC = ({ children, }) => { - const checkAuthCookie = () => { - if (!getCookie("keycloak_cookie") && keycloak?.token) { - setCookie("keycloak_cookie", keycloak.token, 365); - } - }; - if (isAuthRequired) { - return ( - <> - - - Loading... - - - } - isLoadingCheck={(keycloak) => { - if (keycloak.authenticated) { - initInterceptors( - () => - new Promise((resolve, reject) => { - if (keycloak.token) { - if (keycloak.refreshToken) { - keycloak - .updateToken(60) - .then(() => { - deleteCookie("keycloak_cookie"); - checkAuthCookie(); - return resolve(keycloak.token!); - }) - .catch((err) => { - console.log("err", err); - return reject("Failed to refresh token"); - }); - } else return resolve(keycloak.token!); - } else { - keycloak.login(); - reject("Not logged in"); - } - }) - ); - - const kcLocale = (keycloak.tokenParsed as any)["locale"]; - if (kcLocale) { - i18n.changeLanguage(kcLocale); - } - } + React.useEffect(() => { + initInterceptors(); + }, []); - return !keycloak.authenticated; - }} - > - {children} - - - ); - } else { - return ( - <> - }>{children} - - ); - } + return ( + } + > + }>{children} + + ); }; diff --git a/client/src/app/layout/HeaderApp/SSOMenu.tsx b/client/src/app/layout/HeaderApp/SSOMenu.tsx index d57f07ac6d..1279947f9b 100644 --- a/client/src/app/layout/HeaderApp/SSOMenu.tsx +++ b/client/src/app/layout/HeaderApp/SSOMenu.tsx @@ -45,7 +45,8 @@ export const SSOMenu: React.FC = () => { id="sso-actions-toggle" onClick={() => onDropdownToggle(!isDropdownOpen)} > - {(keycloak?.idTokenParsed as any)["preferred_username"]} + {(keycloak?.idTokenParsed as any)?.["preferred_username"] ?? + "DefaultUsername"} )} > @@ -63,8 +64,6 @@ export const SSOMenu: React.FC = () => { id="logout" key="sso_logout" onClick={() => { - // Clears selected persona from storage without updating it in React state so we don't re-render the persona selector while logging out. - // We have to clear it before logout because the redirect can happen before the logout promise resolves. window.localStorage.removeItem( LocalStorageKey.selectedPersona );