fix: Resolve login redirect loop and authentication conflicts
- Add delay after login to ensure session cookies are set properly - Fix client-side auth checks in dashboard to handle session refresh gracefully - Remove conflicting client-side redirects from Navigation component - All authentication now properly handled by unified server-side auth system 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -299,13 +299,52 @@ import ThemeToggle from './ThemeToggle.tsx';
|
|||||||
|
|
||||||
// Check authentication and load user info
|
// Check authentication and load user info
|
||||||
async function initializeNavigation() {
|
async function initializeNavigation() {
|
||||||
const { data: { session } } = await supabase.auth.getSession();
|
// Try to get session, but don't redirect if none found since auth is handled server-side
|
||||||
if (!session) {
|
const { data: { session }, error: sessionError } = await supabase.auth.getSession();
|
||||||
window.location.href = '/';
|
|
||||||
|
if (sessionError || !session) {
|
||||||
|
// Try to get user directly (handles token refresh internally)
|
||||||
|
const { data: { user }, error: userError } = await supabase.auth.getUser();
|
||||||
|
|
||||||
|
if (userError || !user) {
|
||||||
|
console.log('[NAV] No user session found, user likely not authenticated');
|
||||||
|
// Don't redirect - let server-side auth handle this
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load user info
|
// Use the user data we got directly
|
||||||
|
const userName = user.user_metadata.name || user.email;
|
||||||
|
const userEmail = user.email;
|
||||||
|
|
||||||
|
// Update all name displays
|
||||||
|
if (userNameText) userNameText.textContent = userName;
|
||||||
|
if (mobileUserNameText) mobileUserNameText.textContent = userName;
|
||||||
|
if (dropdownName) dropdownName.textContent = userName;
|
||||||
|
if (dropdownEmail) dropdownEmail.textContent = userEmail;
|
||||||
|
|
||||||
|
// Generate user initials
|
||||||
|
const initials = userName.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2);
|
||||||
|
if (userAvatar) userAvatar.textContent = initials;
|
||||||
|
if (mobileUserAvatar) mobileUserAvatar.textContent = initials;
|
||||||
|
if (dropdownAvatar) dropdownAvatar.textContent = initials;
|
||||||
|
|
||||||
|
// Check if user is admin and show admin badge/menu items
|
||||||
|
const { data: userProfile } = await supabase
|
||||||
|
.from('users')
|
||||||
|
.select('role')
|
||||||
|
.eq('id', user.id)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (userProfile?.role === 'admin') {
|
||||||
|
if (adminBadge) adminBadge.classList.remove('hidden');
|
||||||
|
if (mobileAdminBadge) mobileAdminBadge.classList.remove('hidden');
|
||||||
|
if (adminMenuItem) adminMenuItem.classList.remove('hidden');
|
||||||
|
if (mobileAdminMenuItem) mobileAdminMenuItem.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Session exists, use session user
|
||||||
const { data: { user } } = await supabase.auth.getUser();
|
const { data: { user } } = await supabase.auth.getUser();
|
||||||
if (user) {
|
if (user) {
|
||||||
const userName = user.user_metadata.name || user.email;
|
const userName = user.user_metadata.name || user.email;
|
||||||
|
|||||||
@@ -359,32 +359,46 @@ if (!auth) {
|
|||||||
async function loadEvents() {
|
async function loadEvents() {
|
||||||
try {
|
try {
|
||||||
// Get current user (auth already verified server-side)
|
// Get current user (auth already verified server-side)
|
||||||
const { data: { user } } = await supabase.auth.getUser();
|
const { data: { user }, error: userError } = await supabase.auth.getUser();
|
||||||
|
|
||||||
if (!user) {
|
if (userError || !user) {
|
||||||
// This shouldn't happen due to server-side auth, but handle gracefully
|
// Session might be stale, try to refresh and retry once
|
||||||
console.error('No user found despite server-side auth');
|
console.log('[DASHBOARD] No user session found, attempting to refresh...');
|
||||||
loading.innerHTML = `
|
|
||||||
<div class="rounded-xl p-6 max-w-md mx-auto" style="background: var(--error-bg); border: 1px solid var(--error-border);">
|
const { data: refreshData, error: refreshError } = await supabase.auth.refreshSession();
|
||||||
<p class="font-medium" style="color: var(--error-color);">Session error</p>
|
|
||||||
<p class="text-sm mt-2" style="color: var(--error-color); opacity: 0.8;">Please refresh the page</p>
|
if (refreshError || !refreshData?.user) {
|
||||||
</div>
|
// If refresh fails, redirect to login
|
||||||
`;
|
console.error('Session refresh failed, redirecting to login');
|
||||||
|
window.location.href = '/login';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data: userProfile, error: userError } = await supabase
|
// Use refreshed user data
|
||||||
|
console.log('[DASHBOARD] Session refreshed successfully for user:', refreshData.user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the actual user object (either original or refreshed)
|
||||||
|
const currentUser = user || (await supabase.auth.getUser()).data.user;
|
||||||
|
|
||||||
|
if (!currentUser) {
|
||||||
|
console.error('Unable to get user after refresh attempt');
|
||||||
|
window.location.href = '/login';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data: userProfile, error: userProfileError } = await supabase
|
||||||
.from('users')
|
.from('users')
|
||||||
.select('organization_id, role')
|
.select('organization_id, role')
|
||||||
.eq('id', user.id)
|
.eq('id', currentUser.id)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (userError) {
|
if (userProfileError) {
|
||||||
// Error loading user profile
|
// Error loading user profile
|
||||||
loading.innerHTML = `
|
loading.innerHTML = `
|
||||||
<div class="rounded-xl p-6 max-w-md mx-auto" style="background: var(--error-bg); border: 1px solid var(--error-border);">
|
<div class="rounded-xl p-6 max-w-md mx-auto" style="background: var(--error-bg); border: 1px solid var(--error-border);">
|
||||||
<p class="font-medium" style="color: var(--error-color);">Error loading user profile</p>
|
<p class="font-medium" style="color: var(--error-color);">Error loading user profile</p>
|
||||||
<p class="text-sm mt-2" style="color: var(--error-color); opacity: 0.8;">${userError.message || userError}</p>
|
<p class="text-sm mt-2" style="color: var(--error-color); opacity: 0.8;">${userProfileError.message || userProfileError}</p>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -357,13 +357,16 @@ const csrfToken = generateCSRFToken();
|
|||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
const returnTo = urlParams.get('returnTo') || urlParams.get('redirect');
|
const returnTo = urlParams.get('returnTo') || urlParams.get('redirect');
|
||||||
|
|
||||||
// Use the redirectTo from server or fallback to returnTo
|
// Use the redirectTo from server or fallback to returnTo or default dashboard
|
||||||
const finalRedirect = returnTo || result.redirectTo || '/dashboard';
|
const finalRedirect = returnTo || result.redirectTo || '/dashboard';
|
||||||
|
|
||||||
console.log('[LOGIN] Login successful, redirecting to:', finalRedirect);
|
console.log('[LOGIN] Login successful, redirecting to:', finalRedirect);
|
||||||
|
|
||||||
// Use window.location.replace to prevent back button issues
|
// Small delay to ensure session cookies are set properly
|
||||||
window.location.replace(finalRedirect);
|
setTimeout(() => {
|
||||||
|
// Use window.location.href instead of replace to ensure proper navigation
|
||||||
|
window.location.href = finalRedirect;
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorMessage.textContent = (error as Error).message;
|
errorMessage.textContent = (error as Error).message;
|
||||||
|
|||||||
Reference in New Issue
Block a user