Skip to content

Commit 2f8cca6

Browse files
authored
feat(portal): remove InactivityModal and redirect directly to login (#1041)
* feat(aurora): remove InactivityModal and redirect directly to login - Removed InactivityModal component and test file - Updated AuthProvider to navigate directly to "/" on session expiration - Removed showInactivityModal, closeInactivityModal, and redirectAfterModal from AuthContext - All logout reasons (inactive, expired, manual) now redirect immediately - Updated all tests to reflect new behavior * fix(aurora): prevent redirect loop and double navigation on session expiration - Check pathname only (not pathname+search) to avoid redirect loops - Remove duplicate navigation from SessionExpirationTimer (logout() now handles it) - Single source of truth for session expiration navigation
1 parent 97caae8 commit 2f8cca6

11 files changed

Lines changed: 36 additions & 309 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cobaltcore-dev/aurora": minor
3+
---
4+
5+
Remove InactivityModal and redirect directly to login on session expiration

packages/aurora/src/client/components/Auth/InactivityModal.test.tsx

Lines changed: 0 additions & 174 deletions
This file was deleted.

packages/aurora/src/client/components/Auth/InactivityModal.tsx

Lines changed: 0 additions & 30 deletions
This file was deleted.

packages/aurora/src/client/components/Auth/SessionExpirationTimer.tsx

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { useEffect, useState } from "react"
2-
import { useNavigate, useRouterState } from "@tanstack/react-router"
32
import { cn } from "@/client/utils/cn"
43
import { Trans } from "@lingui/react/macro"
54

@@ -9,8 +8,6 @@ export function SessionExpirationTimer(props: {
98
logout: () => Promise<void>
109
}) {
1110
const [timeLeft, setTimeLeft] = useState<string>("")
12-
const navigate = useNavigate()
13-
const pathname = useRouterState({ select: (s) => s.location.pathname })
1411

1512
useEffect(() => {
1613
const updateCountdown = () => {
@@ -45,16 +42,6 @@ export function SessionExpirationTimer(props: {
4542
} catch (error) {
4643
console.error("Error during session expiration logout: ", error)
4744
}
48-
49-
// Only navigate if not already on login page
50-
if (pathname !== "/") {
51-
navigate({
52-
to: "/",
53-
search: {
54-
redirect: pathname || undefined,
55-
},
56-
})
57-
}
5845
}
5946

6047
let logoutTimer: NodeJS.Timeout | undefined
@@ -69,7 +56,7 @@ export function SessionExpirationTimer(props: {
6956
clearInterval(intervalId)
7057
if (logoutTimer) clearTimeout(logoutTimer)
7158
}
72-
}, [props.sessionExpired, props.logout, navigate, pathname])
59+
}, [props.sessionExpired, props.logout])
7360

7461
return (
7562
<div className={cn("text-theme-light pt-2 pb-2 text-xs", props.className)}>

packages/aurora/src/client/routes/__root.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { AuthContext } from "../store/AuthProvider"
66
import { NavigationItem } from "../components/navigation/types"
77
import type { Slots, OnTrackEventCallback } from "../AuroraApp"
88
import styles from "../index.css?inline"
9-
import { InactivityModal } from "../components/Auth/InactivityModal"
109
import { RouteError } from "../components/Error/RouteError"
1110
import { useLingui } from "@lingui/react/macro"
1211
import { StatusError } from "../components/Error/StatusError"
@@ -46,7 +45,6 @@ function AuroraLayout({ children }: { children: React.ReactNode }) {
4645
pageFooter={slots?.pageFooter ? <Slot component={slots.pageFooter} useShadowDOM={false} /> : undefined}
4746
fullWidthContent
4847
>
49-
<InactivityModal />
5048
{children}
5149
</AppShell>
5250
</>

packages/aurora/src/client/store/AuthProvider.test.tsx

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ describe("AuthProvider", () => {
4545

4646
expect(result.current.isAuthenticated).toBe(false)
4747
expect(result.current.user).toBeNull()
48-
expect(result.current.showInactivityModal).toBe(false)
4948
})
5049

5150
it("should throw error when used outside provider", () => {
@@ -147,7 +146,7 @@ describe("AuthProvider", () => {
147146
expect(mockRouter.invalidate).toHaveBeenCalled()
148147
})
149148

150-
it("should invalidate router on inactive logout (no modal)", async () => {
149+
it("should navigate to login on inactive logout", async () => {
151150
const { result } = renderHook(() => useAuth(), { wrapper })
152151

153152
const mockUser: AuthUser = {
@@ -167,11 +166,13 @@ describe("AuthProvider", () => {
167166

168167
expect(result.current.isAuthenticated).toBe(false)
169168
expect(result.current.logoutReason).toBe("inactive")
170-
expect(result.current.showInactivityModal).toBe(false)
171-
expect(mockRouter.invalidate).toHaveBeenCalled()
169+
expect(mockRouter.navigate).toHaveBeenCalledWith({
170+
to: "/",
171+
search: { redirect: "/dashboard" },
172+
})
172173
})
173174

174-
it("should show modal on expired logout", async () => {
175+
it("should navigate to login on expired logout", async () => {
175176
const { result } = renderHook(() => useAuth(), { wrapper })
176177

177178
const mockUser: AuthUser = {
@@ -189,8 +190,12 @@ describe("AuthProvider", () => {
189190
await result.current.logout("expired")
190191
})
191192

192-
expect(result.current.showInactivityModal).toBe(true)
193+
expect(result.current.isAuthenticated).toBe(false)
193194
expect(result.current.logoutReason).toBe("expired")
195+
expect(mockRouter.navigate).toHaveBeenCalledWith({
196+
to: "/",
197+
search: { redirect: "/dashboard" },
198+
})
194199
})
195200
})
196201

@@ -363,7 +368,10 @@ describe("AuthProvider", () => {
363368

364369
expect(result.current.isAuthenticated).toBe(false)
365370
expect(result.current.logoutReason).toBe("expired")
366-
expect(result.current.showInactivityModal).toBe(true)
371+
expect(mockRouter.navigate).toHaveBeenCalledWith({
372+
to: "/",
373+
search: { redirect: "/dashboard" },
374+
})
367375
})
368376

369377
it("should logout immediately if token is already expired", async () => {
@@ -430,8 +438,8 @@ describe("AuthProvider", () => {
430438
})
431439
})
432440

433-
describe("Inactivity Modal", () => {
434-
it("should close modal and navigate to login (expired logout only)", async () => {
441+
describe("Navigation on logout", () => {
442+
it("should navigate to login with redirect on expired logout", async () => {
435443
const { result } = renderHook(() => useAuth(), { wrapper })
436444

437445
const mockUser: AuthUser = {
@@ -449,23 +457,16 @@ describe("AuthProvider", () => {
449457
await result.current.logout("expired")
450458
})
451459

452-
expect(result.current.showInactivityModal).toBe(true)
453-
454-
act(() => {
455-
result.current.closeInactivityModal()
456-
})
457-
458-
expect(result.current.showInactivityModal).toBe(false)
459460
expect(mockRouter.navigate).toHaveBeenCalledWith({
460461
to: "/",
461462
search: { redirect: "/dashboard" },
462463
})
463464
})
464465

465-
it("should navigate without redirect if no path", async () => {
466+
it("should navigate without redirect if on login page", async () => {
466467
Object.defineProperty(window, "location", {
467468
value: {
468-
pathname: "",
469+
pathname: "/",
469470
search: "",
470471
},
471472
writable: true,
@@ -489,10 +490,6 @@ describe("AuthProvider", () => {
489490
await result.current.logout("inactive")
490491
})
491492

492-
act(() => {
493-
result.current.closeInactivityModal()
494-
})
495-
496493
expect(mockRouter.navigate).toHaveBeenCalledWith({
497494
to: "/",
498495
search: undefined,

0 commit comments

Comments
 (0)