Skip to content

Commit eea7835

Browse files
authored
🐛 Fix session expiry when refresh tokens are disabled (#1614)
Resolves https://issues.redhat.com/browse/MTA-1255 - Since we no longer have to support windup reports in a new tab, this code can be cleaned up to use keycloaks internal session management Signed-off-by: ibolton336 <[email protected]>
1 parent e98511e commit eea7835

File tree

3 files changed

+51
-91
lines changed

3 files changed

+51
-91
lines changed
+34-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,43 @@
11
import axios from "axios";
2+
import keycloak from "@app/keycloak";
23

3-
export const initInterceptors = (getToken: () => Promise<string>) => {
4+
export const initInterceptors = () => {
45
axios.interceptors.request.use(
5-
async (config) => {
6-
const token = await getToken();
7-
if (token) config.headers["Authorization"] = "Bearer " + token;
6+
(config) => {
7+
const token = keycloak.token;
8+
if (token) {
9+
config.headers.Authorization = `Bearer ${token}`;
10+
}
811
return config;
912
},
1013
(error) => {
11-
Promise.reject(error);
14+
return Promise.reject(error);
15+
}
16+
);
17+
18+
axios.interceptors.response.use(
19+
(response) => {
20+
return response;
21+
},
22+
async (error) => {
23+
if (error.response && error.response.status === 401) {
24+
try {
25+
const refreshed = await keycloak.updateToken(5);
26+
if (refreshed) {
27+
const retryConfig = {
28+
...error.config,
29+
headers: {
30+
...error.config.headers,
31+
Authorization: `Bearer ${keycloak.token}`,
32+
},
33+
};
34+
return axios(retryConfig);
35+
}
36+
} catch (refreshError) {
37+
keycloak.login();
38+
}
39+
}
40+
return Promise.reject(error);
1241
}
1342
);
1443
};
+15-83
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
import { initInterceptors } from "@app/axios-config";
2-
import { isAuthRequired } from "@app/Constants";
3-
import i18n from "@app/i18n";
1+
import React, { Suspense } from "react";
2+
import { ReactKeycloakProvider } from "@react-keycloak/web";
43
import keycloak from "@app/keycloak";
5-
import { deleteCookie, getCookie, setCookie } from "@app/queries/cookies";
64
import { AppPlaceholder } from "./AppPlaceholder";
7-
import { Flex, FlexItem, Spinner } from "@patternfly/react-core";
8-
import { ReactKeycloakProvider } from "@react-keycloak/web";
9-
import React, { Suspense } from "react";
5+
import { initInterceptors } from "@app/axios-config";
106

117
interface IKeycloakProviderProps {
128
children: React.ReactNode;
@@ -15,81 +11,17 @@ interface IKeycloakProviderProps {
1511
export const KeycloakProvider: React.FC<IKeycloakProviderProps> = ({
1612
children,
1713
}) => {
18-
const checkAuthCookie = () => {
19-
if (!getCookie("keycloak_cookie") && keycloak?.token) {
20-
setCookie("keycloak_cookie", keycloak.token, 365);
21-
}
22-
};
23-
if (isAuthRequired) {
24-
return (
25-
<>
26-
<ReactKeycloakProvider
27-
authClient={keycloak}
28-
initOptions={{ onLoad: "login-required" }}
29-
LoadingComponent={
30-
<Flex
31-
spaceItems={{ default: "spaceItemsSm" }}
32-
alignItems={{ default: "alignItemsCenter" }}
33-
flexWrap={{ default: "nowrap" }}
34-
style={{
35-
width: "100%",
36-
height: "100%",
37-
}}
38-
>
39-
<FlexItem
40-
style={{
41-
margin: "auto auto",
42-
textAlign: "center",
43-
}}
44-
>
45-
<Spinner>Loading...</Spinner>
46-
</FlexItem>
47-
</Flex>
48-
}
49-
isLoadingCheck={(keycloak) => {
50-
if (keycloak.authenticated) {
51-
initInterceptors(
52-
() =>
53-
new Promise<string>((resolve, reject) => {
54-
if (keycloak.token) {
55-
if (keycloak.refreshToken) {
56-
keycloak
57-
.updateToken(60)
58-
.then(() => {
59-
deleteCookie("keycloak_cookie");
60-
checkAuthCookie();
61-
return resolve(keycloak.token!);
62-
})
63-
.catch((err) => {
64-
console.log("err", err);
65-
return reject("Failed to refresh token");
66-
});
67-
} else return resolve(keycloak.token!);
68-
} else {
69-
keycloak.login();
70-
reject("Not logged in");
71-
}
72-
})
73-
);
74-
75-
const kcLocale = (keycloak.tokenParsed as any)["locale"];
76-
if (kcLocale) {
77-
i18n.changeLanguage(kcLocale);
78-
}
79-
}
14+
React.useEffect(() => {
15+
initInterceptors();
16+
}, []);
8017

81-
return !keycloak.authenticated;
82-
}}
83-
>
84-
{children}
85-
</ReactKeycloakProvider>
86-
</>
87-
);
88-
} else {
89-
return (
90-
<>
91-
<Suspense fallback={<AppPlaceholder />}>{children}</Suspense>
92-
</>
93-
);
94-
}
18+
return (
19+
<ReactKeycloakProvider
20+
authClient={keycloak}
21+
initOptions={{ onLoad: "login-required" }}
22+
LoadingComponent={<AppPlaceholder />}
23+
>
24+
<Suspense fallback={<AppPlaceholder />}>{children}</Suspense>
25+
</ReactKeycloakProvider>
26+
);
9527
};

client/src/app/layout/HeaderApp/SSOMenu.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ export const SSOMenu: React.FC = () => {
4545
id="sso-actions-toggle"
4646
onClick={() => onDropdownToggle(!isDropdownOpen)}
4747
>
48-
{(keycloak?.idTokenParsed as any)["preferred_username"]}
48+
{(keycloak?.idTokenParsed as any)?.["preferred_username"] ??
49+
"DefaultUsername"}
4950
</MenuToggle>
5051
)}
5152
>
@@ -63,8 +64,6 @@ export const SSOMenu: React.FC = () => {
6364
id="logout"
6465
key="sso_logout"
6566
onClick={() => {
66-
// Clears selected persona from storage without updating it in React state so we don't re-render the persona selector while logging out.
67-
// We have to clear it before logout because the redirect can happen before the logout promise resolves.
6867
window.localStorage.removeItem(
6968
LocalStorageKey.selectedPersona
7069
);

0 commit comments

Comments
 (0)