fix: Resolve Supabase auth loop and implement secure authentication system
This commit fixes the persistent login/redirect loop issue and implements a robust authentication system for the Docker/localhost environment. Key Changes: - Environment-aware cookie configuration in supabase-ssr.ts - New AuthLoader component to prevent content flashing during auth checks - Cleaned up login page client-side auth logic to prevent redirect loops - Updated dashboard to use AuthLoader for smooth authentication experience Technical Details: - Cookies now use environment-appropriate security settings - Server-side auth verification eliminates client-side timing issues - Loading states provide better UX during auth transitions - Unified authentication pattern across all protected pages Fixes: - Dashboard no longer flashes before auth redirect - Login page loads cleanly without auth checking loops - Cookie configuration works correctly in Docker localhost - No more redirect loops between login and dashboard pages 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import Navigation from '../components/Navigation.astro';
|
||||
import AuthLoader from '../components/AuthLoader.astro';
|
||||
import { verifyAuth } from '../lib/auth';
|
||||
|
||||
// Enable server-side rendering for auth checks
|
||||
@@ -14,6 +15,7 @@ if (!auth) {
|
||||
---
|
||||
|
||||
<Layout title="Dashboard - Black Canyon Tickets">
|
||||
<AuthLoader message="Loading your dashboard...">
|
||||
<style>
|
||||
@keyframes fadeInUp {
|
||||
0% {
|
||||
@@ -829,4 +831,6 @@ if (!auth) {
|
||||
|
||||
// Load events directly (auth already verified server-side)
|
||||
loadEvents();
|
||||
</script>
|
||||
</script>
|
||||
|
||||
</AuthLoader>
|
||||
@@ -293,11 +293,7 @@ const csrfToken = generateCSRFToken();
|
||||
const errorMessage = document.getElementById('error-message') as HTMLDivElement;
|
||||
|
||||
// Debug logging
|
||||
console.log('[LOGIN] Page loaded, checking auth state...');
|
||||
|
||||
// Authentication state
|
||||
let authCheckInProgress = false;
|
||||
let redirectInProgress = false;
|
||||
console.log('[LOGIN] Page loaded, login form ready');
|
||||
|
||||
let isSignUpMode = false;
|
||||
|
||||
@@ -387,103 +383,12 @@ const csrfToken = generateCSRFToken();
|
||||
mainContent.style.display = 'flex';
|
||||
}
|
||||
|
||||
// Safe redirect function with debouncing
|
||||
function safeRedirect(path: string, delay = 500) {
|
||||
if (redirectInProgress) {
|
||||
console.log('[LOGIN] Redirect already in progress, ignoring');
|
||||
return;
|
||||
}
|
||||
|
||||
redirectInProgress = true;
|
||||
console.log(`[LOGIN] Redirecting to ${path} in ${delay}ms...`);
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.pathname = path;
|
||||
}, delay);
|
||||
}
|
||||
|
||||
// Enhanced auth check with better error handling
|
||||
async function checkAuthState() {
|
||||
if (authCheckInProgress) {
|
||||
console.log('[LOGIN] Auth check already in progress');
|
||||
return;
|
||||
}
|
||||
// Note: Auth checking has been moved to server-side only
|
||||
// The login page now focuses solely on the login/signup flow
|
||||
|
||||
authCheckInProgress = true;
|
||||
console.log('[LOGIN] Starting auth check...');
|
||||
|
||||
try {
|
||||
// Get current session with timeout
|
||||
const authPromise = supabase.auth.getSession();
|
||||
const timeoutPromise = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Auth check timeout')), 5000)
|
||||
);
|
||||
|
||||
const { data: { session }, error } = await Promise.race([authPromise, timeoutPromise]) as any;
|
||||
|
||||
if (error) {
|
||||
console.log('[LOGIN] Auth error:', error.message);
|
||||
hideLoading();
|
||||
authCheckInProgress = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
console.log('[LOGIN] No active session, showing login form');
|
||||
hideLoading();
|
||||
authCheckInProgress = false;
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[LOGIN] Active session found, checking organization...');
|
||||
|
||||
// Check if user has an organization with timeout
|
||||
const userPromise = supabase
|
||||
.from('users')
|
||||
.select('organization_id')
|
||||
.eq('id', session.user.id)
|
||||
.single();
|
||||
|
||||
const { data: userData, error: userError } = await Promise.race([
|
||||
userPromise,
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('User data timeout')), 3000)
|
||||
)
|
||||
]) as any;
|
||||
|
||||
if (userError) {
|
||||
console.log('[LOGIN] User data error:', userError.message);
|
||||
hideLoading();
|
||||
authCheckInProgress = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for return URL parameter (support both 'returnTo' and 'redirect')
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const returnTo = urlParams.get('returnTo') || urlParams.get('redirect');
|
||||
|
||||
if (!userData?.organization_id) {
|
||||
console.log('[LOGIN] No organization found, redirecting to onboarding');
|
||||
safeRedirect('/onboarding/organization');
|
||||
} else {
|
||||
console.log('[LOGIN] Organization found, redirecting to', returnTo || '/dashboard');
|
||||
safeRedirect(returnTo || '/dashboard');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('[LOGIN] Auth check failed:', error);
|
||||
hideLoading();
|
||||
authCheckInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip auth check on login page - let the form handle login flow
|
||||
// Initial auth check with delay to prevent flashing
|
||||
// setTimeout(() => {
|
||||
// checkAuthState();
|
||||
// }, 100);
|
||||
|
||||
// Just hide loading and show form immediately on login page
|
||||
// On login page, immediately show the form since auth is handled by the login flow
|
||||
// No need for auth checking on this page - users should be able to login regardless
|
||||
hideLoading();
|
||||
|
||||
</script>
|
||||
Reference in New Issue
Block a user