Skip to content

Commit 914411a

Browse files
TilmanHauptCopilot
andauthored
feat(aurora): unify logout behaviour - remove logoutReason tracking (#1046)
* feat(aurora): simplify logout type - remove logoutReason tracking * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * feat(aurora): simplify logout type - remove logoutReason tracking - Remove logoutReason from AuthContext interface - Simplify logout() to take no parameters - Always call router.invalidate() on logout - Remove "Your session expired" message from login page - Remove duplicate logout timer from SessionExpirationTimer (AuthProvider already handles expiry) --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 2f8cca6 commit 914411a

10 files changed

Lines changed: 45 additions & 93 deletions

File tree

.changeset/simplify-logout-type.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cobaltcore-dev/aurora": patch
3+
---
4+
5+
Simplify logout type - remove logoutReason tracking

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

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@ import { useEffect, useState } from "react"
22
import { cn } from "@/client/utils/cn"
33
import { Trans } from "@lingui/react/macro"
44

5-
export function SessionExpirationTimer(props: {
6-
className?: string
7-
sessionExpired: Date
8-
logout: () => Promise<void>
9-
}) {
5+
export function SessionExpirationTimer(props: { className?: string; sessionExpired: Date }) {
106
const [timeLeft, setTimeLeft] = useState<string>("")
117

128
useEffect(() => {
@@ -34,29 +30,10 @@ export function SessionExpirationTimer(props: {
3430
updateCountdown()
3531
const intervalId = setInterval(updateCountdown, 1000)
3632

37-
const delay = props.sessionExpired.getTime() - Date.now()
38-
39-
const handleExpiration = async () => {
40-
try {
41-
await props.logout()
42-
} catch (error) {
43-
console.error("Error during session expiration logout: ", error)
44-
}
45-
}
46-
47-
let logoutTimer: NodeJS.Timeout | undefined
48-
49-
if (delay <= 0) {
50-
handleExpiration()
51-
} else {
52-
logoutTimer = setTimeout(handleExpiration, delay)
53-
}
54-
5533
return () => {
5634
clearInterval(intervalId)
57-
if (logoutTimer) clearTimeout(logoutTimer)
5835
}
59-
}, [props.sessionExpired, props.logout])
36+
}, [props.sessionExpired])
6037

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

packages/aurora/src/client/components/navigation/UserMenu.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export function UserMenu() {
2929
console.error("Error logging out: ", error)
3030
} finally {
3131
try {
32-
await logout("manual")
32+
await logout()
3333
} catch (error) {
3434
console.error("Error during logout: ", error)
3535
}
@@ -59,7 +59,7 @@ export function UserMenu() {
5959
/>
6060
{expiresAt && (
6161
<PopupMenuSectionHeading>
62-
<SessionExpirationTimer sessionExpired={expiresAt} logout={() => logout("expired")} />
62+
<SessionExpirationTimer sessionExpired={expiresAt} />
6363
</PopupMenuSectionHeading>
6464
)}
6565
</PopupMenuSection>

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

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ const textinputstyles = `
6868
`
6969

7070
export function AuthLoginPage() {
71-
const { isAuthenticated, login, logoutReason } = useAuth()
71+
const { isAuthenticated, login } = useAuth()
7272
const isLoading = useRouterState({ select: (s) => s.isLoading })
7373
const navigate = Route.useNavigate()
7474
const search = Route.useSearch()
@@ -165,8 +165,6 @@ export function AuthLoginPage() {
165165

166166
const isLoggingIn = isLoading || isSubmitting
167167

168-
const wasInactive = logoutReason === "inactive" || logoutReason === "expired"
169-
170168
if (slots?.login && !isAuthenticated && !isLoading) {
171169
return (
172170
<div className="mt-8 flex justify-center">
@@ -192,13 +190,9 @@ export function AuthLoginPage() {
192190
<p className="mb-6 text-center">
193191
<Trans>Enter your credentials to access your account</Trans>
194192
</p>
195-
{(search.redirect || wasInactive) && (
193+
{search.redirect && (
196194
<p className="mb-4 text-center text-sm text-red-500">
197-
{wasInactive ? (
198-
<Trans>Your session expired. Please login again.</Trans>
199-
) : (
200-
<Trans>You need to login to access this page.</Trans>
201-
)}
195+
<Trans>You need to login to access this page.</Trans>
202196
</p>
203197
)}
204198
<form

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

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -107,22 +107,22 @@ describe("AuthProvider", () => {
107107
})
108108

109109
await act(async () => {
110-
await result.current.logout("manual")
110+
await result.current.logout()
111111
})
112112

113-
expect(result.current.logoutReason).toBe("manual")
113+
expect(result.current.isAuthenticated).toBe(false)
114114

115115
// Login again
116116
await act(async () => {
117117
await result.current.login(mockUser)
118118
})
119119

120-
expect(result.current.logoutReason).toBeUndefined()
120+
expect(result.current.isAuthenticated).toBe(true)
121121
})
122122
})
123123

124124
describe("Logout", () => {
125-
it("should logout with manual reason and invalidate router", async () => {
125+
it("should logout and invalidate router", async () => {
126126
const { result } = renderHook(() => useAuth(), { wrapper })
127127

128128
const mockUser: AuthUser = {
@@ -137,16 +137,15 @@ describe("AuthProvider", () => {
137137
})
138138

139139
await act(async () => {
140-
await result.current.logout("manual")
140+
await result.current.logout()
141141
})
142142

143143
expect(result.current.isAuthenticated).toBe(false)
144144
expect(result.current.user).toBeNull()
145-
expect(result.current.logoutReason).toBe("manual")
146145
expect(mockRouter.invalidate).toHaveBeenCalled()
147146
})
148147

149-
it("should navigate to login on inactive logout", async () => {
148+
it("should navigate to login on logout", async () => {
150149
const { result } = renderHook(() => useAuth(), { wrapper })
151150

152151
const mockUser: AuthUser = {
@@ -161,11 +160,10 @@ describe("AuthProvider", () => {
161160
})
162161

163162
await act(async () => {
164-
await result.current.logout("inactive")
163+
await result.current.logout()
165164
})
166165

167166
expect(result.current.isAuthenticated).toBe(false)
168-
expect(result.current.logoutReason).toBe("inactive")
169167
expect(mockRouter.navigate).toHaveBeenCalledWith({
170168
to: "/",
171169
search: { redirect: "/dashboard" },
@@ -187,11 +185,10 @@ describe("AuthProvider", () => {
187185
})
188186

189187
await act(async () => {
190-
await result.current.logout("expired")
188+
await result.current.logout()
191189
})
192190

193191
expect(result.current.isAuthenticated).toBe(false)
194-
expect(result.current.logoutReason).toBe("expired")
195192
expect(mockRouter.navigate).toHaveBeenCalledWith({
196193
to: "/",
197194
search: { redirect: "/dashboard" },
@@ -222,7 +219,6 @@ describe("AuthProvider", () => {
222219
})
223220

224221
expect(result.current.isAuthenticated).toBe(true)
225-
expect(result.current.logoutReason).toBeUndefined()
226222
})
227223

228224
it("should NOT reset inactivity timer on user activity (timer removed)", async () => {
@@ -257,7 +253,6 @@ describe("AuthProvider", () => {
257253

258254
// Should still be authenticated — inactivity timer removed
259255
expect(result.current.isAuthenticated).toBe(true)
260-
expect(result.current.logoutReason).toBeUndefined()
261256
})
262257

263258
it("should NOT detect various activity events (timer removed)", async () => {
@@ -306,7 +301,6 @@ describe("AuthProvider", () => {
306301

307302
// Should remain unauthenticated without any logout reason
308303
expect(result.current.isAuthenticated).toBe(false)
309-
expect(result.current.logoutReason).toBeUndefined()
310304
})
311305

312306
it("should clear inactivity timer on manual logout", async () => {
@@ -330,16 +324,16 @@ describe("AuthProvider", () => {
330324

331325
// Manual logout
332326
await act(async () => {
333-
await result.current.logout("manual")
327+
await result.current.logout()
334328
})
335329

336330
// Advance past what would have been the inactivity timeout
337331
await act(async () => {
338332
vi.advanceTimersByTime(60 * 60 * 1000)
339333
})
340334

341-
// Should have logged out with manual reason, not inactive
342-
expect(result.current.logoutReason).toBe("manual")
335+
// Should have logged out
336+
expect(result.current.isAuthenticated).toBe(false)
343337
})
344338
})
345339

@@ -367,7 +361,6 @@ describe("AuthProvider", () => {
367361
})
368362

369363
expect(result.current.isAuthenticated).toBe(false)
370-
expect(result.current.logoutReason).toBe("expired")
371364
expect(mockRouter.navigate).toHaveBeenCalledWith({
372365
to: "/",
373366
search: { redirect: "/dashboard" },
@@ -390,7 +383,6 @@ describe("AuthProvider", () => {
390383
})
391384

392385
expect(result.current.isAuthenticated).toBe(false)
393-
expect(result.current.logoutReason).toBe("expired")
394386
})
395387

396388
it("should clear logout timer on new login", async () => {
@@ -454,7 +446,7 @@ describe("AuthProvider", () => {
454446
})
455447

456448
await act(async () => {
457-
await result.current.logout("expired")
449+
await result.current.logout()
458450
})
459451

460452
expect(mockRouter.navigate).toHaveBeenCalledWith({
@@ -487,7 +479,7 @@ describe("AuthProvider", () => {
487479
})
488480

489481
await act(async () => {
490-
await result.current.logout("inactive")
482+
await result.current.logout()
491483
})
492484

493485
expect(mockRouter.navigate).toHaveBeenCalledWith({
@@ -498,7 +490,7 @@ describe("AuthProvider", () => {
498490
})
499491

500492
describe("Edge Cases", () => {
501-
it("should handle default logout reason", async () => {
493+
it("should handle logout", async () => {
502494
const { result } = renderHook(() => useAuth(), { wrapper })
503495

504496
const mockUser: AuthUser = {
@@ -516,7 +508,7 @@ describe("AuthProvider", () => {
516508
await result.current.logout()
517509
})
518510

519-
expect(result.current.logoutReason).toBe("manual")
511+
expect(result.current.isAuthenticated).toBe(false)
520512
})
521513

522514
it("should handle rapid login/logout cycles", async () => {
@@ -537,7 +529,7 @@ describe("AuthProvider", () => {
537529
expect(result.current.isAuthenticated).toBe(true)
538530

539531
await act(async () => {
540-
await result.current.logout("manual")
532+
await result.current.logout()
541533
})
542534

543535
expect(result.current.isAuthenticated).toBe(false)

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

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@ export type User = TokenData["user"] | null
66
export interface AuthContext {
77
isAuthenticated: boolean
88
login: (user: User, expires_at?: string) => Promise<void>
9-
logout: (reason?: "inactive" | "expired" | "manual") => Promise<void>
9+
logout: () => Promise<void>
1010
user?: User
1111
expiresAt?: Date
12-
logoutReason?: "inactive" | "expired" | "manual"
1312
}
1413

1514
interface RouterNavigation {
@@ -22,7 +21,6 @@ const AuthContext = React.createContext<AuthContext | null>(null)
2221
export function AuthProvider({ children, router }: { children: React.ReactNode; router: RouterNavigation }) {
2322
const [user, setUser] = React.useState<User | null>(null)
2423
const [expiresAt, setExpiresAt] = React.useState<Date | undefined>(undefined)
25-
const [logoutReason, setLogoutReason] = React.useState<"inactive" | "expired" | "manual" | undefined>(undefined)
2624

2725
const logoutTimerRef = useRef<NodeJS.Timeout | null>(null)
2826

@@ -35,32 +33,25 @@ export function AuthProvider({ children, router }: { children: React.ReactNode;
3533
}
3634
}, [])
3735

38-
const logout = useCallback(
39-
async (reason: "inactive" | "expired" | "manual" = "manual") => {
40-
clearLogoutTimer()
36+
const logout = useCallback(async () => {
37+
clearLogoutTimer()
4138

42-
setUser(null)
43-
setExpiresAt(undefined)
44-
setLogoutReason(reason)
39+
setUser(null)
40+
setExpiresAt(undefined)
4541

46-
// Navigate to login page with redirect (only if not already on login page)
47-
const currentPath = window.location.pathname !== "/" ? window.location.pathname + window.location.search : null
42+
// Navigate to login page; include a redirect back to the current path when not already on "/"
43+
const currentPath = window.location.pathname !== "/" ? window.location.pathname + window.location.search : null
4844

49-
router.navigate({
50-
to: "/",
51-
search: currentPath ? { redirect: currentPath } : undefined,
52-
})
45+
router.navigate({
46+
to: "/",
47+
search: currentPath ? { redirect: currentPath } : undefined,
48+
})
5349

54-
if (reason === "manual") {
55-
router.invalidate()
56-
}
57-
},
58-
[router, clearLogoutTimer]
59-
)
50+
router.invalidate()
51+
}, [router, clearLogoutTimer])
6052

6153
const login = useCallback(async (user: User, expires_at?: string) => {
6254
setUser(user)
63-
setLogoutReason(undefined)
6455

6556
if (expires_at) {
6657
const expiration = new Date(expires_at)
@@ -77,12 +68,12 @@ export function AuthProvider({ children, router }: { children: React.ReactNode;
7768
const timeUntilExpiry = expiresAt.getTime() - Date.now()
7869

7970
if (timeUntilExpiry <= 0) {
80-
logout("expired")
71+
logout()
8172
return
8273
}
8374

8475
logoutTimerRef.current = setTimeout(() => {
85-
logout("expired")
76+
logout()
8677
}, timeUntilExpiry)
8778
}
8879

@@ -99,7 +90,6 @@ export function AuthProvider({ children, router }: { children: React.ReactNode;
9990
login,
10091
logout,
10192
expiresAt,
102-
logoutReason,
10393
}}
10494
>
10595
{children}

packages/aurora/src/locales/de/messages.po

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3646,9 +3646,6 @@ msgstr ""
36463646
msgid "Your S3 credentials are invalid or expired. Please try creating new credentials or contact your administrator."
36473647
msgstr ""
36483648

3649-
msgid "Your session expired. Please login again."
3650-
msgstr "Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an."
3651-
36523649
msgid "Your session has expired. Please log in again."
36533650
msgstr "Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an."
36543651

packages/aurora/src/locales/de/messages.ts

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

packages/aurora/src/locales/en/messages.po

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3646,9 +3646,6 @@ msgstr "Your credentials are valid but you don't have permission to perform this
36463646
msgid "Your S3 credentials are invalid or expired. Please try creating new credentials or contact your administrator."
36473647
msgstr "Your S3 credentials are invalid or expired. Please try creating new credentials or contact your administrator."
36483648

3649-
msgid "Your session expired. Please login again."
3650-
msgstr "Your session expired. Please login again."
3651-
36523649
msgid "Your session has expired. Please log in again."
36533650
msgstr "Your session has expired. Please log in again."
36543651

packages/aurora/src/locales/en/messages.ts

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)