diff --git a/client/src/app/axios-config/apiInit.ts b/client/src/app/axios-config/apiInit.ts index 913ab563df..06e3c97e4e 100644 --- a/client/src/app/axios-config/apiInit.ts +++ b/client/src/app/axios-config/apiInit.ts @@ -1,43 +1,14 @@ import axios from "axios"; -import keycloak from "@app/keycloak"; -export const initInterceptors = () => { +export const initInterceptors = (getToken: () => Promise) => { axios.interceptors.request.use( - (config) => { - const token = keycloak.token; - if (token) { - config.headers.Authorization = `Bearer ${token}`; - } + async (config) => { + const token = await getToken(); + if (token) config.headers["Authorization"] = "Bearer " + token; return config; }, (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); + Promise.reject(error); } ); }; diff --git a/client/src/app/components/KeycloakProvider.tsx b/client/src/app/components/KeycloakProvider.tsx index 363be14fed..daf2ed50f5 100644 --- a/client/src/app/components/KeycloakProvider.tsx +++ b/client/src/app/components/KeycloakProvider.tsx @@ -1,8 +1,12 @@ -import React, { Suspense } from "react"; -import { ReactKeycloakProvider } from "@react-keycloak/web"; +import { initInterceptors } from "@app/axios-config"; +import { isAuthRequired } from "@app/Constants"; +import i18n from "@app/i18n"; import keycloak from "@app/keycloak"; +import { deleteCookie, getCookie, setCookie } from "@app/queries/cookies"; import { AppPlaceholder } from "./AppPlaceholder"; -import { initInterceptors } from "@app/axios-config"; +import { Flex, FlexItem, Spinner } from "@patternfly/react-core"; +import { ReactKeycloakProvider } from "@react-keycloak/web"; +import React, { Suspense } from "react"; interface IKeycloakProviderProps { children: React.ReactNode; @@ -11,17 +15,81 @@ interface IKeycloakProviderProps { export const KeycloakProvider: React.FC = ({ children, }) => { - React.useEffect(() => { - initInterceptors(); - }, []); + 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); + } + } - return ( - } - > - }>{children} - - ); + return !keycloak.authenticated; + }} + > + {children} + + + ); + } else { + return ( + <> + }>{children} + + ); + } }; diff --git a/client/src/app/layout/HeaderApp/SSOMenu.tsx b/client/src/app/layout/HeaderApp/SSOMenu.tsx index 1279947f9b..d57f07ac6d 100644 --- a/client/src/app/layout/HeaderApp/SSOMenu.tsx +++ b/client/src/app/layout/HeaderApp/SSOMenu.tsx @@ -45,8 +45,7 @@ export const SSOMenu: React.FC = () => { id="sso-actions-toggle" onClick={() => onDropdownToggle(!isDropdownOpen)} > - {(keycloak?.idTokenParsed as any)?.["preferred_username"] ?? - "DefaultUsername"} + {(keycloak?.idTokenParsed as any)["preferred_username"]} )} > @@ -64,6 +63,8 @@ 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 );