From 963d255ea2f61126b2269b9fb50ac607ca021e6e Mon Sep 17 00:00:00 2001 From: Hanzo_dev <2002samudiojohan@gmail.com> Date: Wed, 25 Feb 2026 22:38:03 -0500 Subject: [PATCH] fix: auth flow - logout nuclear, router redirige admin a panel, login sin bloqueo BD --- frontend/src/components/AppHeader.vue | 3 +- frontend/src/components/auth/LoginForm.vue | 16 +++++---- frontend/src/router/index.ts | 38 ++++++++++++-------- frontend/src/stores/auth.ts | 40 +++++++++++++++++++--- 4 files changed, 69 insertions(+), 28 deletions(-) diff --git a/frontend/src/components/AppHeader.vue b/frontend/src/components/AppHeader.vue index aeb45fa..528e909 100644 --- a/frontend/src/components/AppHeader.vue +++ b/frontend/src/components/AppHeader.vue @@ -146,9 +146,8 @@ const openReportModal = () => { } const handleLogout = () => { - authStore.logout() showMenu.value = false - router.push('/login') + authStore.logout() } diff --git a/frontend/src/components/auth/LoginForm.vue b/frontend/src/components/auth/LoginForm.vue index dc27e43..92c7c21 100644 --- a/frontend/src/components/auth/LoginForm.vue +++ b/frontend/src/components/auth/LoginForm.vue @@ -17,16 +17,18 @@ const authStore = useAuthStore() const handleLogin = async () => { isLoading.value = true errorMessage.value = '' - console.log('Iniciando Login con correo...') try { await authStore.login(email.value.trim().toLowerCase(), password.value) - - // Esperar a que el perfil se cargue globalmente para saber a qué pantalla navegar - setTimeout(() => { - const r = authStore.role || 'PASSENGER' - navigateByUserRole(r) - }, 800) + + // Esperar hasta que el perfil esté cargado (máx 3 segundos) + let attempts = 0 + while (!authStore.role && attempts < 30) { + await new Promise(res => setTimeout(res, 100)) + attempts++ + } + + navigateByUserRole(authStore.role || 'PASSENGER') } catch (error: any) { console.error('Error Login detallado:', error) diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 5704dc3..c37e2be 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -147,12 +147,13 @@ const router = createRouter({ }) router.beforeEach(async (to, _from, next) => { - // Rutas públicas: siempre pasan sin verificación - if (!to.meta.requiresAuth && !to.meta.role) { + // Rutas completamente públicas (splash, login) + const publicRoutes = ['/login', '/splash', '/'] + if (publicRoutes.includes(to.path)) { return next() } - // Leer sesión activa de Supabase (sustituye el viejo localStorage) + // Leer sesión activa de Supabase const { data: { session } } = await supabase.auth.getSession() // Ruta protegida sin sesión → redirige a login @@ -160,8 +161,8 @@ router.beforeEach(async (to, _from, next) => { return next('/login') } - // Ruta con restricción de rol → verificar en la BD - if (to.meta.role && session) { + // Si hay sesión, obtener el rol real del usuario + if (session) { const { data: profile } = await supabase .from('users') .select('role') @@ -169,16 +170,25 @@ router.beforeEach(async (to, _from, next) => { .single() const userRole = profile?.role?.toUpperCase() || 'PASSENGER' - const allowedRoles = Array.isArray(to.meta.role) - ? (to.meta.role as string[]).map(r => r.toUpperCase()) - : [(to.meta.role as string).toUpperCase()] - if (!allowedRoles.includes(userRole)) { - // Redirigir a la vista correcta según su rol real - if (userRole === 'ADMIN') return next('/admin') - else if (userRole === 'DRIVER') return next('/driver') - else if (userRole === 'PROMOTER') return next('/promoter') - else return next('/map') + // Si el admin entra a rutas de pasajero → redirigir al panel admin + const passengerRoutes = ['/map', '/schedule', '/discover', '/transport', '/favorites', '/profile', '/coupons'] + if (userRole === 'ADMIN' && passengerRoutes.some(r => to.path.startsWith(r))) { + return next('/admin') + } + + // Ruta con restricción de rol → verificar permiso + if (to.meta.role) { + const allowedRoles = Array.isArray(to.meta.role) + ? (to.meta.role as string[]).map(r => r.toUpperCase()) + : [(to.meta.role as string).toUpperCase()] + + if (!allowedRoles.includes(userRole)) { + if (userRole === 'ADMIN') return next('/admin') + else if (userRole === 'DRIVER') return next('/driver') + else if (userRole === 'PROMOTER') return next('/promoter') + else return next('/map') + } } } diff --git a/frontend/src/stores/auth.ts b/frontend/src/stores/auth.ts index d974710..136a70e 100644 --- a/frontend/src/stores/auth.ts +++ b/frontend/src/stores/auth.ts @@ -36,16 +36,46 @@ export const useAuthStore = defineStore('auth', () => { }) async function login(email: string, pass: string) { - // Use standard Supabase signIn - const { error } = await supabase.auth.signInWithPassword({ email, password: pass }) + const { data, error } = await supabase.auth.signInWithPassword({ email, password: pass }) if (error) throw new Error(error.message) + + if (data.user) { + // Establecer sesión y rol inmediatamente desde el JWT (sin esperar BD) + userSession.value = data.session + // Usar user_metadata como perfil temporal para que el rol esté listo al instante + userProfile.value = { + id: data.user.id, + email: data.user.email, + full_name: data.user.user_metadata?.full_name || data.user.email, + role: data.user.user_metadata?.role || 'PASSENGER' + } + // Cargar perfil completo de BD en background (sin bloquear navegación) + supabase.from('users').select('*').eq('id', data.user.id).single() + .then(({ data: profile }) => { if (profile) userProfile.value = profile }) + .catch(() => { }) + } } - async function logout() { - await supabase.auth.signOut() + function logout() { + // Limpiar estado de Pinia inmediatamente userSession.value = null userProfile.value = null - window.location.href = '/' + + // Limpiar TODA la sesión del navegador sin esperar a Supabase + localStorage.clear() + sessionStorage.clear() + + // Limpiar IndexedDB donde Supabase guarda tokens + try { + indexedDB.deleteDatabase('supabase-js-auth') + indexedDB.deleteDatabase('supabase') + } catch (_) { } + + // Llamar signOut en background (sin await - no bloqueamos) + supabase.auth.signOut().catch(() => { }) + + // Redirigir forzando recarga completa del navegador + window.location.replace('/login') } return {