fix: Remove client-side auth from ProtectedRoute causing redirect loops

- Disabled client-side auth checks in ProtectedRoute component
- Added server-side auth to onboarding/organization.astro
- ProtectedRoute now acts as simple wrapper, auth handled server-side
- Resolves setup screen → home redirect loop issue

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-12 20:45:12 -06:00
parent 425dfc9348
commit b34de627a9
2 changed files with 19 additions and 128 deletions

View File

@@ -1,5 +1,8 @@
--- ---
// Simple protected route component // DEPRECATED: Protected route component - now handled server-side
// This component no longer performs authentication checks.
// All authentication is handled by the unified auth system at the server level.
export interface Props { export interface Props {
title?: string; title?: string;
requireAdmin?: boolean; requireAdmin?: boolean;
@@ -8,135 +11,13 @@ export interface Props {
const { title = "Protected Page", requireAdmin = false } = Astro.props; const { title = "Protected Page", requireAdmin = false } = Astro.props;
--- ---
<!-- Simple wrapper - auth is handled server-side by unified auth system -->
<div class="auth-wrapper"> <div class="auth-wrapper">
<slot /> <slot />
</div> </div>
<script> <script>
import { supabase } from '../lib/supabase'; // Note: Authentication is now handled server-side by the unified auth system.
// This component is kept for backwards compatibility but no longer performs auth checks.
// State tracking to prevent loops console.log('[PROTECTED] ProtectedRoute mounted - auth handled server-side');
let authVerificationInProgress = false;
let redirectInProgress = false;
console.log('[PROTECTED] ProtectedRoute mounted on:', window.location.pathname);
// Safe redirect with loop prevention
function safeRedirectToLogin() {
if (redirectInProgress) {
console.log('[PROTECTED] Redirect already in progress, skipping');
return;
}
// Don't redirect if we're already on login page
if (window.location.pathname === '/login') {
console.log('[PROTECTED] Already on login page, not redirecting');
return;
}
redirectInProgress = true;
console.log('[PROTECTED] Redirecting to login...');
setTimeout(() => {
const returnTo = encodeURIComponent(window.location.pathname);
window.location.href = `/login?returnTo=${returnTo}`;
}, 100);
}
// Enhanced auth verification with timeout and retry logic
async function verifyAuth() {
if (authVerificationInProgress) {
console.log('[PROTECTED] Auth verification already in progress');
return;
}
authVerificationInProgress = true;
console.log('[PROTECTED] Starting auth verification...');
try {
// Add timeout to prevent hanging
const authPromise = supabase.auth.getSession();
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Auth verification timeout')), 8000)
);
const { data: { session }, error } = await Promise.race([authPromise, timeoutPromise]) as any;
if (error) {
console.warn('[PROTECTED] Auth verification failed:', error.message);
safeRedirectToLogin();
return;
}
if (!session) {
console.warn('[PROTECTED] No session found, redirecting to login');
safeRedirectToLogin();
return;
}
console.log('[PROTECTED] Auth verification successful');
// Store auth token for API calls
const authToken = session.access_token;
if (authToken) {
// Set default authorization header for fetch requests
const originalFetch = window.fetch;
window.fetch = function(url, options = {}) {
if (!options.headers) {
options.headers = {};
}
// Add auth header and credentials to API calls
if (typeof url === 'string' && url.startsWith('/api/')) {
options.headers['Authorization'] = `Bearer ${authToken}`;
options.credentials = 'include';
}
return originalFetch(url, options);
};
}
authVerificationInProgress = false;
} catch (error) {
console.error('[PROTECTED] Auth verification error:', error);
authVerificationInProgress = false;
safeRedirectToLogin();
}
}
// Delayed auth verification to prevent race conditions
setTimeout(() => {
verifyAuth();
}, 200);
// Listen for auth state changes with debouncing
let authChangeTimeout: number | null = null;
supabase.auth.onAuthStateChange((event, session) => {
console.log('[PROTECTED] Auth state change:', event);
// Clear previous timeout
if (authChangeTimeout) {
clearTimeout(authChangeTimeout);
}
// Debounce auth state changes
authChangeTimeout = setTimeout(() => {
if (event === 'SIGNED_OUT' || !session) {
console.log('[PROTECTED] User signed out, redirecting to login');
safeRedirectToLogin();
}
}, 500);
});
</script> </script>
<style>
.auth-wrapper {
min-height: 100vh;
}
/* Add loading state styles */
.auth-loading {
opacity: 0.5;
pointer-events: none;
}
</style>

View File

@@ -1,6 +1,16 @@
--- ---
import SecureLayout from '../../layouts/SecureLayout.astro'; import SecureLayout from '../../layouts/SecureLayout.astro';
import ProtectedRoute from '../../components/ProtectedRoute.astro'; import ProtectedRoute from '../../components/ProtectedRoute.astro';
import { verifyAuth } from '../../lib/auth-unified';
// Enable server-side rendering for auth checks
export const prerender = false;
// Server-side authentication check
const auth = await verifyAuth(Astro.cookies);
if (!auth) {
return Astro.redirect('/login');
}
--- ---
<ProtectedRoute> <ProtectedRoute>