From ac4c9e8389790508ea41ea26bd94046e761365de Mon Sep 17 00:00:00 2001 From: Hanzo_dev <2002samudiojohan@gmail.com> Date: Tue, 24 Feb 2026 20:52:11 -0500 Subject: [PATCH] Fix Google Login CORS and role-based redirection --- backend/app/main.py | 11 +++++-- frontend/src/components/auth/LoginForm.vue | 18 +++++++++-- frontend/src/components/auth/RegisterForm.vue | 22 +++++++++++--- frontend/src/firebaseConfig.ts | 16 ++++++---- frontend/src/views/AuthView.vue | 30 ++++++++++++++++++- 5 files changed, 83 insertions(+), 14 deletions(-) diff --git a/backend/app/main.py b/backend/app/main.py index 117bd49..6e38926 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -110,12 +110,19 @@ app = FastAPI( ) # CORS middleware -origins = ["*"] # Permitir todos para producción corregida - +origins = [ + "http://localhost:5173", + "http://127.0.0.1:5173", + "https://sibu-frontend.vercel.app", + "https://sibu-transport.web.app", + "https://sibu2-0-transport-2026.firebaseapp.com", + "https://sibu2-0-transport-2026.web.app", +] app.add_middleware( CORSMiddleware, allow_origins=origins, + allow_origin_regex="https://.*sibu.*\.vercel\.app", # Permitir subdominios de vercel con 'sibu' allow_credentials=True, allow_methods=["*"], allow_headers=["*"], diff --git a/frontend/src/components/auth/LoginForm.vue b/frontend/src/components/auth/LoginForm.vue index a74c0eb..164c985 100644 --- a/frontend/src/components/auth/LoginForm.vue +++ b/frontend/src/components/auth/LoginForm.vue @@ -50,6 +50,14 @@ const handleLogin = async () => { } } +const navigateByUserRole = (role: string) => { + const r = role.toUpperCase() + if (r === 'ADMIN') router.push('/admin') + else if (r === 'DRIVER') router.push('/driver') + else if (r === 'PROMOTER') router.push('/promoter') + else router.push('/map') +} + const handleGoogleLogin = async () => { isLoading.value = true errorMessage.value = '' @@ -63,11 +71,17 @@ const handleGoogleLogin = async () => { const response = await authService.googleLogin(token) console.log('Backend Google login exitoso:', response) authStore.login(response.access_token, response.role, response.full_name) - router.push('/map') + + // Navigate based on actual role from backend + navigateByUserRole(response.role) } } catch (error: any) { console.error('Error Google Login:', error) - errorMessage.value = `Error con Google: ${error.message || 'Error desconocido'}` + if (error.response?.data?.detail) { + errorMessage.value = `Error: ${error.response.data.detail}` + } else { + errorMessage.value = `Error con Google: ${error.message || 'Error desconocido'}` + } } finally { isLoading.value = false } diff --git a/frontend/src/components/auth/RegisterForm.vue b/frontend/src/components/auth/RegisterForm.vue index fbd5c39..b8cdaad 100644 --- a/frontend/src/components/auth/RegisterForm.vue +++ b/frontend/src/components/auth/RegisterForm.vue @@ -54,7 +54,7 @@ const handleRegister = async () => { // Redirigir casi de inmediato setTimeout(() => { - router.push('/map') + navigateByUserRole(response.role) }, 1000) } catch (error: any) { @@ -71,6 +71,14 @@ const handleRegister = async () => { } } +const navigateByUserRole = (role: string) => { + const r = role.toUpperCase() + if (r === 'ADMIN') router.push('/admin') + else if (r === 'DRIVER') router.push('/driver') + else if (r === 'PROMOTER') router.push('/promoter') + else router.push('/map') +} + const handleGoogleRegister = async () => { isLoading.value = true errorMessage.value = '' @@ -82,7 +90,7 @@ const handleGoogleRegister = async () => { if (token) { const response = await authService.googleLogin(token) - console.log('Backend Google login exitoso:', response) + console.log('Backend Google login/register exitoso:', response) analyticsService.logEvent({ event_name: 'sign_up', @@ -90,12 +98,18 @@ const handleGoogleRegister = async () => { }) authStore.login(response.access_token, response.role, response.full_name) - router.push('/map') + + // Navigate based on actual role from backend + navigateByUserRole(response.role) } } catch (error: any) { console.error('Error Google Register:', error) - errorMessage.value = `Error con Google: ${error.message || 'Intenta de nuevo'}` + if (error.response?.data?.detail) { + errorMessage.value = `Error: ${error.response.data.detail}` + } else { + errorMessage.value = `Error con Google: ${error.message || 'Intenta de nuevo'}` + } } finally { isLoading.value = false } diff --git a/frontend/src/firebaseConfig.ts b/frontend/src/firebaseConfig.ts index 1970102..6f77cfb 100644 --- a/frontend/src/firebaseConfig.ts +++ b/frontend/src/firebaseConfig.ts @@ -19,6 +19,9 @@ const firebaseConfig = { const app = initializeApp(firebaseConfig); export const auth = getAuth(app); export const googleProvider = new GoogleAuthProvider(); +googleProvider.setCustomParameters({ + prompt: 'select_account' +}); // Detect if the user is on a mobile device const isMobile = () => { @@ -35,11 +38,13 @@ export const signInWithGoogle = async (): Promise<{ user: any; token: string }> if (isMobile()) { console.log("DEBUG: Intentando signInWithRedirect..."); await signInWithRedirect(auth, googleProvider); + // On mobile, the page reloads, so we return a promise that doesn't resolve + // here as the app will re-initialize on the callback URL. return new Promise(() => { }); } else { console.log("DEBUG: Intentando signInWithPopup..."); const result = await signInWithPopup(auth, googleProvider); - const token = await result.user.getIdToken(); + const token = await result.user.getIdToken(true); // Force refresh to get fresh token return { user: result.user, token }; } } catch (error: any) { @@ -49,20 +54,21 @@ export const signInWithGoogle = async (): Promise<{ user: any; token: string }> }; /** - * Call this once when the app loads (e.g., in App.vue or main.ts) to + * Call this once when the app loads (e.g., in AuthView.vue) to * handle the result from a Google redirect login on mobile. - * Returns null if there is no pending redirect result. */ export const getGoogleRedirectResult = async (): Promise<{ user: any; token: string } | null> => { try { const result = await getRedirectResult(auth); if (result && result.user) { - const token = await result.user.getIdToken(); + const token = await result.user.getIdToken(true); + console.log("DEBUG: Redirect result obtained for:", result.user.email); return { user: result.user, token }; } return null; } catch (error: any) { console.error("DEBUG: Error en getRedirectResult:", error); - return null; + // Throw the error so the UI can catch it and show a message + throw error; } }; diff --git a/frontend/src/views/AuthView.vue b/frontend/src/views/AuthView.vue index 849e099..7715207 100644 --- a/frontend/src/views/AuthView.vue +++ b/frontend/src/views/AuthView.vue @@ -11,15 +11,17 @@ const isLogin = ref(true) const toggleAuth = () => { isLogin.value = !isLogin.value } const router = useRouter() const authStore = useAuthStore() +const redirectErrorMessage = ref('') // Handle redirect result from Google Sign-In on mobile -// (signInWithRedirect reloads the page; we catch the result here on load) onMounted(async () => { try { const result = await getGoogleRedirectResult() if (result) { + console.log('Procesando resultado de redirección de Google...') const response = await authService.googleLogin(result.token) authStore.login(response.access_token, response.role, response.full_name) + const role = response.role.toUpperCase() if (role === 'ADMIN') router.push('/admin') else if (role === 'DRIVER') router.push('/driver') @@ -28,6 +30,7 @@ onMounted(async () => { } } catch (e: any) { console.error('Google redirect result error:', e) + redirectErrorMessage.value = `Error al volver de Google: ${e.message || 'Error desconocido'}` } }) @@ -70,6 +73,12 @@ onMounted(async () => { + +
+ warning + {{ redirectErrorMessage }} +
+ @@ -180,6 +189,25 @@ onMounted(async () => { border-bottom-color: var(--active-color); } +/* ─── Errores de Redirección ─── */ +.redirect-error { + margin: 1rem; + padding: 0.75rem 1rem; + background: rgba(239, 68, 68, 0.1); + border: 1px solid rgba(239, 68, 68, 0.2); + border-radius: 0.75rem; + color: #ef4444; + font-size: 0.8125rem; + font-weight: 600; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.redirect-error .material-icons { + font-size: 1.125rem; +} + /* ─── Transición entre formularios ─── */ .auth-slide-enter-active, .auth-slide-leave-active {