fix: use getSession() in useAuth hook (fixes 10s timeout)

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) <noreply@anthropic.com>
This commit is contained in:
Arnas Donauskas 2026-04-16 20:10:49 +03:00
parent aaaddf0d5a
commit 8f0f8afae3
No known key found for this signature in database

View File

@ -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);