# Authentication Loading State Fixes ## Problem Users were experiencing an infinite "loading" state when accessing protected routes like `/dashboard`. The page would show "Loading..." indefinitely instead of either displaying the content or redirecting to login. ## Root Cause Analysis The issue was caused by a mismatch between multiple authentication state management systems: 1. **AuthContext**: Managed session state and loading flag 2. **AuthorizationBlocker**: Determined access reasons based on session state 3. **AuthGateway**: Combined context and blocker state 4. **RouteGuard**: Handled UI rendering and redirects ### The Problem Flow: ``` 1. User visits /dashboard 2. AuthContext initializes: session = null, loading = false 3. AuthGuard checks access 4. AuthorizationBlocker sees session = null → returns 'loading' 5. AuthGateway sees blocker.reason = 'loading' → sets isLoading = true 6. RouteGuard shows loading state 7. Session fetch completes: session = null, loading = false 8. But blocker still returns 'loading' because session is null 9. Infinite loading state ``` ## Fixes Applied ### 1. AuthContext.tsx **Problem**: Initial loading state was `false`, but session fetch wasn't tracked **Fix**: ```typescript // Before const [loading, setLoading] = useState(false); const fetchSession = useCallback(async () => { try { const current = await sessionService.getSession(); setSession(current); } catch { setSession(null); } }, [sessionService]); // After const [loading, setLoading] = useState(true); // Start with loading = true const fetchSession = useCallback(async () => { setLoading(true); // Set loading when starting fetch try { const current = await sessionService.getSession(); setSession(current); } catch { setSession(null); } finally { setLoading(false); // Clear loading when done } }, [sessionService]); ``` ### 2. AuthGateway.ts **Problem**: Was checking both `authContext.loading` AND `blocker.reason === 'loading'` **Fix**: Only check authContext.loading for the isLoading state ```typescript // Before isLoading: this.authContext.loading || reason === 'loading', // After isLoading: this.authContext.loading, ``` ### 3. AuthorizationBlocker.ts **Problem**: Returned 'loading' when session was null, creating confusion **Fix**: Treat null session as unauthenticated, not loading ```typescript // Before getReason(): AuthorizationBlockReason { if (!this.currentSession) { return 'loading'; } // ... } // After getReason(): AuthorizationBlockReason { if (!this.currentSession) { return 'unauthenticated'; // Null = unauthenticated } // ... } canExecute(): boolean { const reason = this.getReason(); return reason === 'enabled'; // Only enabled grants access } ``` ### 4. RouteGuard.tsx **Problem**: Generic loading message, unclear redirect flow **Fix**: Better user feedback during authentication flow ```typescript // Loading state shows verification message if (accessState.isLoading) { return loadingComponent || (
); } // Unauthorized shows redirect message before redirecting if (!accessState.canAccess && config.redirectOnUnauthorized !== false) { return (
); } ``` ### 5. Dashboard Page **Problem**: Had redundant auth checks that conflicted with layout protection **Fix**: Simplified to only handle data loading ```typescript // Before: Had auth checks, useEffect for redirects, etc. export default function DashboardPage() { const router = useRouter(); const { session, loading: authLoading } = useAuth(); // ... complex auth logic // After: Only handles data loading export default function DashboardPage() { const { data: dashboardData, isLoading, error } = useDashboardOverview(); // ... simple data loading } ``` ## New Authentication Flow ### Unauthenticated User: 1. User visits `/dashboard` 2. Middleware checks for `gp_session` cookie → not found 3. Middleware redirects to `/auth/login?returnTo=/dashboard` 4. User logs in 5. Session created, cookie set 6. Redirected back to `/dashboard` 7. AuthGuard verifies session exists 8. Dashboard loads ### Authenticated User: 1. User visits `/dashboard` 2. Middleware checks for `gp_session` cookie → found 3. Request proceeds to page rendering 4. AuthGuard shows "Verifying authentication..." (briefly) 5. Session verified via AuthContext 6. AuthGuard shows "Redirecting to login..." (if unauthorized) 7. Or renders dashboard content ### Loading State Resolution: ``` Initial: session=null, loading=true → AuthGuard shows "Verifying..." Fetch completes: session=null, loading=false → AuthGuard redirects to login ``` ## Files Modified 1. `apps/website/lib/auth/AuthContext.tsx` - Fixed loading state management 2. `apps/website/lib/gateways/AuthGateway.ts` - Simplified isLoading logic 3. `apps/website/lib/blockers/AuthorizationBlocker.ts` - Removed 'loading' reason 4. `apps/website/lib/gateways/RouteGuard.tsx` - Improved user feedback 5. `apps/website/app/dashboard/page.tsx` - Removed redundant auth checks 6. `apps/website/app/dashboard/layout.tsx` - Added AuthGuard protection 7. `apps/website/app/profile/layout.tsx` - Added AuthGuard protection 8. `apps/website/app/sponsor/layout.tsx` - Added AuthGuard protection 9. `apps/website/app/onboarding/layout.tsx` - Added AuthGuard protection 10. `apps/website/app/admin/layout.tsx` - Added RouteGuard protection ## Testing the Fix ### Expected Behavior: - **Unauthenticated access**: Redirects to login within 500ms - **Authenticated access**: Shows dashboard after brief verification - **No infinite loading**: Loading states resolve properly ### Test Scenarios: 1. Clear cookies, visit `/dashboard` → Should redirect to login 2. Login, visit `/dashboard` → Should show dashboard 3. Login, clear cookies, refresh → Should redirect to login 4. Login as non-admin, visit `/admin` → Should redirect to login ## Security Notes - **Defense in depth**: Multiple protection layers (middleware + layout + page) - **No security bypass**: All fixes maintain security requirements - **User experience**: Clear feedback during authentication flow - **Performance**: Minimal overhead, only necessary checks ## Future Improvements 1. Add role-based access to SessionViewModel 2. Implement proper backend role system 3. Add session refresh mechanism 4. Implement proper token validation 5. Add authentication state persistence