From dea4a6c9ed90e568dff12137dda57bb3e9e51443 Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Wed, 5 Nov 2025 02:36:14 +0000 Subject: [PATCH] Fix session persistence - improve token restoration cgen-47838b8ee0e941d4845ff07ce0b2a7fd --- client/contexts/AuthContext.tsx | 63 ++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/client/contexts/AuthContext.tsx b/client/contexts/AuthContext.tsx index e2f1e6d6..da6f7b1e 100644 --- a/client/contexts/AuthContext.tsx +++ b/client/contexts/AuthContext.tsx @@ -165,11 +165,15 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ const storageClearedRef = useRef(false); useEffect(() => { + let sessionRestored = false; + // Add timeout to ensure loading doesn't get stuck const loadingTimeout = setTimeout(() => { console.log("Auth loading timeout - forcing loading to false"); - setLoading(false); - }, 5000); + if (!sessionRestored) { + setLoading(false); + } + }, 8000); if (!storageClearedRef.current && typeof window !== "undefined") { try { @@ -189,29 +193,51 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ } } - // Get initial session - supabase.auth - .getSession() - .then(({ data: { session } }) => { - clearTimeout(loadingTimeout); - setSession(session); - setUser(session?.user ?? null); + // Helper to check if auth tokens exist in localStorage + const hasAuthTokens = () => { + if (typeof window === "undefined") return false; + const keys = Object.keys(window.localStorage); + return keys.some((key) => key.includes("auth-token") || key.includes("sb-") && key.includes("-auth")); + }; + + // Get initial session with persistence recovery + const initializeAuth = async () => { + try { + const { data: { session } } = await supabase.auth.getSession(); + + // If no session but tokens exist, the session might not have restored yet + // Wait a bit for onAuthStateChange to trigger + if (!session && hasAuthTokens()) { + console.log("Tokens exist in storage but session not yet restored, waiting..."); + // Don't set loading to false yet - wait for onAuthStateChange + return; + } + if (session?.user) { - fetchUserProfile(session.user.id); + sessionRestored = true; + setSession(session); + setUser(session.user); + await fetchUserProfile(session.user.id); } else { + sessionRestored = true; setLoading(false); } - }) - .catch((error) => { - clearTimeout(loadingTimeout); + } catch (error) { console.error("Error getting session:", error); + sessionRestored = true; setLoading(false); - }); + } + }; - // Listen for auth changes + initializeAuth(); + + // Listen for auth changes - this is the source of truth const { data: { subscription }, } = supabase.auth.onAuthStateChange(async (event, session) => { + console.log("Auth state change:", event, !!session?.user); + + sessionRestored = true; setSession(session); setUser(session?.user ?? null); @@ -220,8 +246,8 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ } else { setProfile(null); setRoles([]); + setLoading(false); } - setLoading(false); // Handle token refresh failures specifically if (event === "TOKEN_REFRESH_FAILED") { @@ -257,7 +283,10 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ } }); - return () => subscription.unsubscribe(); + return () => { + clearTimeout(loadingTimeout); + subscription.unsubscribe(); + }; }, []); const fetchUserProfile = async (