1
+ /**
2
+ * AI Assistance Disclosure:
3
+ * Tool: GitHub Copilot (model: Claude Sonnet 4), date: 2025-09-16
4
+ * Purpose: To create a simple client-side auth guard that prevents browser back navigation to cached protected pages after logout.
5
+ * Author Review: I validated correctness, security, and performance of the code.
6
+ *
7
+ */
8
+
9
+ /**
10
+ * Simple client-side auth guard that checks for token on page navigation.
11
+ * This catches cases where browser back button bypasses middleware cache controls.
12
+ */
13
+
14
+ "use client" ;
15
+
16
+ import { useEffect } from "react" ;
17
+ import { usePathname , useRouter } from "next/navigation" ;
18
+ import { getToken } from "@/services/userServiceCookies" ;
19
+
20
+ export default function AuthGuard ( ) {
21
+ const pathname = usePathname ( ) ;
22
+ const router = useRouter ( ) ;
23
+
24
+ // Check token immediately on mount and pathname changes
25
+ useEffect ( ( ) => {
26
+ // Skip auth routes
27
+ if ( pathname . startsWith ( "/auth" ) ) {
28
+ return ;
29
+ }
30
+
31
+ // Check for token
32
+ const token = getToken ( ) ;
33
+ if ( ! token ) {
34
+ console . log ( "AuthGuard: No token found, redirecting to login" ) ;
35
+ // Use setTimeout to ensure this runs after render
36
+ setTimeout ( ( ) => {
37
+ router . push ( "/auth/login" ) ;
38
+ } , 0 ) ;
39
+ }
40
+ } , [ pathname , router ] ) ;
41
+
42
+ // Add popstate listener specifically for browser back/forward navigation
43
+ useEffect ( ( ) => {
44
+ const handlePopState = ( ) => {
45
+ if ( ! pathname . startsWith ( "/auth" ) ) {
46
+ const token = getToken ( ) ;
47
+ if ( ! token ) {
48
+ console . log ( "AuthGuard: No token found on popstate (back/forward), redirecting to login" ) ;
49
+ router . push ( "/auth/login" ) ;
50
+ }
51
+ }
52
+ } ;
53
+
54
+ window . addEventListener ( "popstate" , handlePopState ) ;
55
+ return ( ) => window . removeEventListener ( "popstate" , handlePopState ) ;
56
+ } , [ pathname , router ] ) ;
57
+
58
+ // Add visibility change listener to catch tab switching scenarios
59
+ useEffect ( ( ) => {
60
+ const handleVisibilityChange = ( ) => {
61
+ if ( ! document . hidden && ! pathname . startsWith ( "/auth" ) ) {
62
+ const token = getToken ( ) ;
63
+ if ( ! token ) {
64
+ console . log ( "AuthGuard: No token found on visibility change, redirecting to login" ) ;
65
+ router . push ( "/auth/login" ) ;
66
+ }
67
+ }
68
+ } ;
69
+
70
+ document . addEventListener ( "visibilitychange" , handleVisibilityChange ) ;
71
+ return ( ) => document . removeEventListener ( "visibilitychange" , handleVisibilityChange ) ;
72
+ } , [ pathname , router ] ) ;
73
+
74
+ // Add focus listener for additional protection
75
+ useEffect ( ( ) => {
76
+ const handleFocus = ( ) => {
77
+ if ( ! pathname . startsWith ( "/auth" ) ) {
78
+ const token = getToken ( ) ;
79
+ if ( ! token ) {
80
+ console . log ( "AuthGuard: No token found on focus, redirecting to login" ) ;
81
+ router . push ( "/auth/login" ) ;
82
+ }
83
+ }
84
+ } ;
85
+
86
+ window . addEventListener ( "focus" , handleFocus ) ;
87
+ return ( ) => window . removeEventListener ( "focus" , handleFocus ) ;
88
+ } , [ pathname , router ] ) ;
89
+
90
+ return null ; // This component renders nothing
91
+ }
0 commit comments