Skip to content

Commit 554e04e

Browse files
authored
COMP-797 logout user if session expires (#773)
1 parent ec81232 commit 554e04e

File tree

5 files changed

+102
-13
lines changed

5 files changed

+102
-13
lines changed

compliance-web/src/App.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import PopoverProvider from "@/components/Shared/Popover/PopoverProvider";
1313
import { LocalizationProvider } from "@mui/x-date-pickers";
1414
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
1515

16-
const queryClient = new QueryClient();
16+
const queryClient = new QueryClient({
17+
defaultOptions: { queries: { refetchOnWindowFocus: false } },
18+
});
1719

1820
function App() {
1921
const environment = AppConfig.environment;

compliance-web/src/routeTree.gen.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
1010

1111
import { Route as rootRouteImport } from './routes/__root'
12+
import { Route as SessionExpiredRouteImport } from './routes/session-expired'
1213
import { Route as OidcCallbackRouteImport } from './routes/oidc-callback'
1314
import { Route as AuthenticatedRouteImport } from './routes/_authenticated'
1415
import { Route as IndexRouteImport } from './routes/index'
@@ -26,6 +27,11 @@ import { Route as AuthenticatedCeDatabaseInspectionsInspectionNumberRouteImport
2627
import { Route as AuthenticatedCeDatabaseComplaintsComplaintNumberRouteImport } from './routes/_authenticated/ce-database/complaints/$complaintNumber'
2728
import { Route as AuthenticatedCeDatabaseCaseFilesCaseFileNumberRouteImport } from './routes/_authenticated/ce-database/case-files/$caseFileNumber'
2829

30+
const SessionExpiredRoute = SessionExpiredRouteImport.update({
31+
id: '/session-expired',
32+
path: '/session-expired',
33+
getParentRoute: () => rootRouteImport,
34+
} as any)
2935
const OidcCallbackRoute = OidcCallbackRouteImport.update({
3036
id: '/oidc-callback',
3137
path: '/oidc-callback',
@@ -121,6 +127,7 @@ const AuthenticatedCeDatabaseCaseFilesCaseFileNumberRoute =
121127
export interface FileRoutesByFullPath {
122128
'/': typeof IndexRoute
123129
'/oidc-callback': typeof OidcCallbackRoute
130+
'/session-expired': typeof SessionExpiredRoute
124131
'/review-board': typeof AuthenticatedReviewBoardRoute
125132
'/admin/agencies': typeof AuthenticatedAdminAgenciesRoute
126133
'/admin/proponents': typeof AuthenticatedAdminProponentsRoute
@@ -138,6 +145,7 @@ export interface FileRoutesByFullPath {
138145
export interface FileRoutesByTo {
139146
'/': typeof IndexRoute
140147
'/oidc-callback': typeof OidcCallbackRoute
148+
'/session-expired': typeof SessionExpiredRoute
141149
'/review-board': typeof AuthenticatedReviewBoardRoute
142150
'/admin/agencies': typeof AuthenticatedAdminAgenciesRoute
143151
'/admin/proponents': typeof AuthenticatedAdminProponentsRoute
@@ -157,6 +165,7 @@ export interface FileRoutesById {
157165
'/': typeof IndexRoute
158166
'/_authenticated': typeof AuthenticatedRouteWithChildren
159167
'/oidc-callback': typeof OidcCallbackRoute
168+
'/session-expired': typeof SessionExpiredRoute
160169
'/_authenticated/review-board': typeof AuthenticatedReviewBoardRoute
161170
'/_authenticated/admin/agencies': typeof AuthenticatedAdminAgenciesRoute
162171
'/_authenticated/admin/proponents': typeof AuthenticatedAdminProponentsRoute
@@ -176,6 +185,7 @@ export interface FileRouteTypes {
176185
fullPaths:
177186
| '/'
178187
| '/oidc-callback'
188+
| '/session-expired'
179189
| '/review-board'
180190
| '/admin/agencies'
181191
| '/admin/proponents'
@@ -193,6 +203,7 @@ export interface FileRouteTypes {
193203
to:
194204
| '/'
195205
| '/oidc-callback'
206+
| '/session-expired'
196207
| '/review-board'
197208
| '/admin/agencies'
198209
| '/admin/proponents'
@@ -211,6 +222,7 @@ export interface FileRouteTypes {
211222
| '/'
212223
| '/_authenticated'
213224
| '/oidc-callback'
225+
| '/session-expired'
214226
| '/_authenticated/review-board'
215227
| '/_authenticated/admin/agencies'
216228
| '/_authenticated/admin/proponents'
@@ -230,10 +242,18 @@ export interface RootRouteChildren {
230242
IndexRoute: typeof IndexRoute
231243
AuthenticatedRoute: typeof AuthenticatedRouteWithChildren
232244
OidcCallbackRoute: typeof OidcCallbackRoute
245+
SessionExpiredRoute: typeof SessionExpiredRoute
233246
}
234247

235248
declare module '@tanstack/react-router' {
236249
interface FileRoutesByPath {
250+
'/session-expired': {
251+
id: '/session-expired'
252+
path: '/session-expired'
253+
fullPath: '/session-expired'
254+
preLoaderRoute: typeof SessionExpiredRouteImport
255+
parentRoute: typeof rootRouteImport
256+
}
237257
'/oidc-callback': {
238258
id: '/oidc-callback'
239259
path: '/oidc-callback'
@@ -397,6 +417,7 @@ const rootRouteChildren: RootRouteChildren = {
397417
IndexRoute: IndexRoute,
398418
AuthenticatedRoute: AuthenticatedRouteWithChildren,
399419
OidcCallbackRoute: OidcCallbackRoute,
420+
SessionExpiredRoute: SessionExpiredRoute,
400421
}
401422
export const routeTree = rootRouteImport
402423
._addFileChildren(rootRouteChildren)

compliance-web/src/router/RouterProviderWithAuthContext.tsx

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { RouterProvider } from "@tanstack/react-router";
22
import { useAuth } from "react-oidc-context";
33
import router from "./router";
4-
import { useEffect } from "react";
4+
import { useEffect, useState } from "react";
55
import { useStaffUserValidation } from "@/hooks/useAuthorization";
66
import { trackAnalytics } from "@epic/centre-analytics";
77
import { AppConfig } from "@/utils/config";
@@ -16,38 +16,52 @@ declare module "@tanstack/react-router" {
1616
export default function RouterProviderWithAuthContext() {
1717
const authentication = useAuth();
1818
const { isAccessDenied, preferredUsername } = useStaffUserValidation();
19+
const [signedOut, setSignedOut] = useState(false);
1920

2021
useEffect(() => {
2122
const removeExpiring = authentication.events.addAccessTokenExpiring(() => {
2223
// eslint-disable-next-line no-console
2324
console.log("AccessTokenExpiring: Refreshing token");
24-
try{
25+
try {
2526
authentication.signinSilent();
2627
} catch (error) {
2728
// eslint-disable-next-line no-console
2829
console.error("Silent renew failed", error);
29-
authentication.signoutRedirect();
30+
setSignedOut(true);
3031
}
3132
});
3233

33-
const removeSilentRenewError = authentication.events.addSilentRenewError((error) => {
34-
// eslint-disable-next-line no-console
35-
console.log("Silent renew failed, logging out", error);
36-
authentication.signoutRedirect();
37-
});
34+
const removeSilentRenewError = authentication.events.addSilentRenewError(
35+
(error) => {
36+
// eslint-disable-next-line no-console
37+
console.log("Silent renew failed, logging out", error);
38+
setSignedOut(true);
39+
},
40+
);
3841

3942
const removeExpired = authentication.events.addAccessTokenExpired(() => {
4043
// eslint-disable-next-line no-console
4144
console.log("Token expired, logging out");
42-
authentication.signoutRedirect();
45+
setSignedOut(true);
4346
});
4447

4548
return () => {
4649
removeExpiring();
4750
removeSilentRenewError();
4851
removeExpired();
4952
};
50-
}, [authentication, authentication.events, authentication.signinSilent, authentication.signoutRedirect]);
53+
}, [
54+
authentication,
55+
authentication.events,
56+
authentication.signinSilent,
57+
authentication.signoutRedirect,
58+
]);
59+
60+
useEffect(() => {
61+
if (signedOut) {
62+
router.navigate({ to: "/session-expired" });
63+
}
64+
}, [signedOut]);
5165

5266
// Show access denied if user is authenticated but not a valid staff user
5367
if (authentication.isAuthenticated && isAccessDenied) {
@@ -57,7 +71,7 @@ export default function RouterProviderWithAuthContext() {
5771
<p>You are not authorized to access this application.</p>
5872
<p>User ID: {preferredUsername}</p>
5973
<p>Please contact your administrator to get access.</p>
60-
<button
74+
<button
6175
onClick={() => authentication.signoutRedirect()}
6276
style={{
6377
padding: "0.5rem 1rem",
@@ -66,7 +80,7 @@ export default function RouterProviderWithAuthContext() {
6680
color: "white",
6781
border: "none",
6882
borderRadius: "4px",
69-
cursor: "pointer"
83+
cursor: "pointer",
7084
}}
7185
>
7286
Sign Out

compliance-web/src/routes/__root.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ function Layout() {
2626

2727
const matches = useMatches();
2828

29+
const isSessionExpired = matches.some((route) => route.routeId === "/session-expired");
30+
2931
const isProfileLayout = matches.some((route) =>
3032
[
3133
"/_authenticated/ce-database/case-files/$caseFileNumber",
@@ -40,6 +42,23 @@ function Layout() {
4042
}
4143
}, [setAppHeaderHeight]);
4244

45+
// If session expired, show only the content without header/sidebar
46+
if (isSessionExpired) {
47+
return (
48+
<Box
49+
sx={{
50+
display: "flex",
51+
justifyContent: "center",
52+
alignItems: "center",
53+
height: "100vh",
54+
width: "100vw",
55+
}}
56+
>
57+
<Outlet />
58+
</Box>
59+
);
60+
}
61+
4362
return (
4463
<>
4564
<EAOAppBar ref={appBarRef} />
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Box, Button, Typography } from "@mui/material";
2+
import { createFileRoute } from "@tanstack/react-router";
3+
import { BCDesignTokens } from "epic.theme";
4+
import { useAuth } from "react-oidc-context";
5+
6+
export const Route = createFileRoute("/session-expired")({
7+
component: SessionExpired,
8+
});
9+
10+
function SessionExpired() {
11+
const authentication = useAuth();
12+
13+
return (
14+
<Box
15+
sx={{ padding: "2rem", textAlign: "center" }}
16+
gap={2}
17+
display="flex"
18+
flexDirection="column"
19+
alignItems="center"
20+
>
21+
<Typography
22+
variant="h1"
23+
sx={{ color: BCDesignTokens.typographyColorLink }}
24+
>
25+
Session Expired
26+
</Typography>
27+
<Typography variant="body1">
28+
Your session has expired. Please sign in again to continue.
29+
</Typography>
30+
<Button onClick={() => authentication.signinRedirect()}>Sign In</Button>
31+
</Box>
32+
);
33+
}

0 commit comments

Comments
 (0)