From 8f0f8afae3e2b455ac505c0edc19530c7f7a1f19 Mon Sep 17 00:00:00 2001 From: Arnas Donauskas Date: Thu, 16 Apr 2026 20:10:49 +0300 Subject: [PATCH] fix: use getSession() in useAuth hook (fixes 10s timeout) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #5 switched client components to getSession() but missed src/hooks/use-auth.ts. The auth hook was still calling supabase.auth.getUser() which makes a network round-trip to Supabase, and on some deployments the request was hanging long enough to trigger the 10-second safety timeout: [browser] [useAuth] getUser() timed out after 10s, clearing loading state This caused the dashboard to sit on the loading spinner for the full 10 seconds on every page load. Changes: - Replaced getUser() with getSession() in init() — reads from localStorage synchronously (no network). - Reduced safety timer from 10s to 3s. getSession() shouldn't take more than a few hundred ms; 3s is a reasonable ceiling. Security note (same reasoning as PR #5): - The middleware server-side STILL uses getUser() to verify the JWT against Supabase on every request, so unauthenticated users can't reach protected routes. - RLS on the database is the actual authorization boundary. - getSession() on the client is only used to decide what UI to render. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/hooks/use-auth.ts | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/hooks/use-auth.ts b/src/hooks/use-auth.ts index f0e9ae7..8d353ae 100644 --- a/src/hooks/use-auth.ts +++ b/src/hooks/use-auth.ts @@ -20,13 +20,14 @@ export function useAuth() { const supabase = createClient(); let mounted = true; - // Safety net: if something hangs, surface it instead of an infinite spinner. + // Safety net: 3s is plenty for localStorage reads. If it hangs here, + // something else is wrong (likely storage permissions in an iframe). const safetyTimer = setTimeout(() => { if (mounted) { - console.warn("[useAuth] getUser() timed out after 10s, clearing loading state"); + console.warn("[useAuth] getSession() timed out after 3s, clearing loading state"); setLoading(false); } - }, 10000); + }, 3000); const fetchProfile = async (userId: string) => { try { @@ -57,23 +58,25 @@ export function useAuth() { const init = async () => { try { + // getSession() reads from localStorage — instant, no network call. + // The middleware already validates the JWT server-side with getUser() + // on every request, and RLS enforces authorization at the DB level, + // so the client can trust the local session for UI purposes. const { - data: { user }, + data: { session }, error, - } = await supabase.auth.getUser(); + } = await supabase.auth.getSession(); if (error) { - // AuthSessionMissingError is expected when not logged in — don't log as error - if (error.name !== "AuthSessionMissingError") { - console.error("[useAuth] getUser error:", error.message); - } + console.error("[useAuth] getSession error:", error.message); } if (!mounted) return; - setUser(user); + const currentUser = session?.user ?? null; + setUser(currentUser); - if (user) { - await fetchProfile(user.id); + if (currentUser) { + await fetchProfile(currentUser.id); } } catch (err) { console.error("[useAuth] init threw:", err);