diff --git a/src/App.tsx b/src/App.tsx index a1c0a18..27b7448 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,9 @@ + import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; import { Suspense, lazy } from 'react'; import { Toaster } from "@/components/ui/sonner"; import Auth from './pages/Auth'; +import EmailConfirmation from './pages/EmailConfirmation'; import ProtectedRoute from './components/auth/ProtectedRoute'; import { authStore } from './stores/authStore'; @@ -31,6 +33,12 @@ function App() { element={isLoggedIn ? : } /> + {/* Email confirmation route - should be accessible without authentication */} + } + /> + {/* Protected routes */} diff --git a/src/components/auth/AuthSecurityFeatures.tsx b/src/components/auth/AuthSecurityFeatures.tsx new file mode 100644 index 0000000..863293c --- /dev/null +++ b/src/components/auth/AuthSecurityFeatures.tsx @@ -0,0 +1,32 @@ + +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Shield, Mail, Lock } from 'lucide-react'; + +const AuthSecurityFeatures = () => { + return ( +
+ + + + Segurança reforçada: Sua conta está protegida contra tentativas de acesso não autorizado. + + + + + + + Confirmação por email: Você receberá um link de confirmação para ativar sua conta. + + + + + + + Proteção contra força bruta: Sistema automaticamente bloqueia tentativas suspeitas. + + +
+ ); +}; + +export default AuthSecurityFeatures; diff --git a/src/components/auth/RegisterForm.tsx b/src/components/auth/RegisterForm.tsx index d346220..1312625 100644 --- a/src/components/auth/RegisterForm.tsx +++ b/src/components/auth/RegisterForm.tsx @@ -49,7 +49,7 @@ const RegisterForm = ({ isLoading, setIsLoading }: RegisterFormProps) => { email, password: senha, options: { - emailRedirectTo: `${window.location.origin}/`, + emailRedirectTo: `${window.location.origin}/email-confirmation`, data: { nome, empresa: empresa || null, @@ -69,7 +69,7 @@ const RegisterForm = ({ isLoading, setIsLoading }: RegisterFormProps) => { if (data.user.identities && data.user.identities.length === 0) { console.log('👤 Usuário já existe, reenviando confirmação'); toast.info("E-mail já cadastrado. Verifique sua caixa de entrada!", { - description: "Enviamos um novo link para você definir sua senha e acessar sua conta. Não se esqueça de checar a pasta de spam.", + description: "Enviamos um novo link de confirmação para você ativar sua conta. Não se esqueça de checar a pasta de spam.", duration: 10000, }); } else { @@ -94,11 +94,17 @@ const RegisterForm = ({ isLoading, setIsLoading }: RegisterFormProps) => { console.error('❌ Erro crítico no webhook para n8n:', error); }); + // Show success message and redirect to login + toast.success("Cadastro realizado com sucesso!", { + description: "📧 Enviamos um link de confirmação para seu email. Clique no link para ativar sua conta e fazer login.", + duration: 10000, + }); + // Redirect to login page with success message navigate('/auth', { state: { showSuccessMessage: true, - message: "✅ Cadastro realizado com sucesso! Agora, faça o login com o e-mail e a senha que você acabou de criar." + message: "✅ Cadastro realizado! Verifique seu email e clique no link de confirmação para ativar sua conta." } }); } diff --git a/src/components/auth/SocialLoginButtons.tsx b/src/components/auth/SocialLoginButtons.tsx new file mode 100644 index 0000000..09fe488 --- /dev/null +++ b/src/components/auth/SocialLoginButtons.tsx @@ -0,0 +1,96 @@ + +import { useState } from 'react'; +import { Button } from "@/components/ui/button"; +import { toast } from "sonner"; +import { supabase } from '@/integrations/supabase/client'; + +const SocialLoginButtons = () => { + const [loadingGoogle, setLoadingGoogle] = useState(false); + + const handleGoogleLogin = async () => { + setLoadingGoogle(true); + + try { + console.log('🔐 Iniciando login com Google...'); + + const { error } = await supabase.auth.signInWithOAuth({ + provider: 'google', + options: { + redirectTo: `${window.location.origin}/`, + queryParams: { + access_type: 'offline', + prompt: 'consent', + } + } + }); + + if (error) { + console.error('❌ Erro no login com Google:', error); + toast.error("Erro no login com Google", { + description: error.message || "Não foi possível conectar com o Google. Tente novamente.", + }); + } + // Note: Se não houver erro, o usuário será redirecionado automaticamente + } catch (error) { + console.error('❌ Erro geral no login com Google:', error); + toast.error("Erro no login", { + description: "Ocorreu um erro inesperado. Tente novamente.", + }); + } finally { + setLoadingGoogle(false); + } + }; + + return ( +
+
+
+ +
+
+ + Ou continue com + +
+
+ + +
+ ); +}; + +export default SocialLoginButtons; diff --git a/src/pages/Auth.tsx b/src/pages/Auth.tsx index eb29d96..3f192ca 100644 --- a/src/pages/Auth.tsx +++ b/src/pages/Auth.tsx @@ -5,6 +5,8 @@ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import LoginForm from '@/components/auth/LoginForm'; import RegisterForm from '@/components/auth/RegisterForm'; +import SocialLoginButtons from '@/components/auth/SocialLoginButtons'; +import AuthSecurityFeatures from '@/components/auth/AuthSecurityFeatures'; import { toast } from "sonner"; const Auth = () => { @@ -12,10 +14,10 @@ const Auth = () => { const [activeTab, setActiveTab] = useState("login"); const location = useLocation(); - // Show success message when redirected from registration + // Show success message when redirected from registration or email confirmation useEffect(() => { if (location.state?.showSuccessMessage && location.state?.message) { - toast.success("Cadastro realizado!", { + toast.success("Sucesso!", { description: location.state.message, duration: 8000, }); @@ -40,12 +42,15 @@ const Auth = () => { Cadastro - + + - + + + diff --git a/src/pages/EmailConfirmation.tsx b/src/pages/EmailConfirmation.tsx new file mode 100644 index 0000000..e463099 --- /dev/null +++ b/src/pages/EmailConfirmation.tsx @@ -0,0 +1,126 @@ + +import { useEffect, useState } from 'react'; +import { useNavigate, useSearchParams } from 'react-router-dom'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { CheckCircle, XCircle, Loader2 } from 'lucide-react'; +import { supabase } from '@/integrations/supabase/client'; +import { toast } from "sonner"; + +const EmailConfirmation = () => { + const [searchParams] = useSearchParams(); + const navigate = useNavigate(); + const [status, setStatus] = useState<'loading' | 'success' | 'error'>('loading'); + const [message, setMessage] = useState(''); + + useEffect(() => { + const handleEmailConfirmation = async () => { + try { + // Pegar os tokens da URL + const token_hash = searchParams.get('token_hash'); + const type = searchParams.get('type'); + + if (token_hash && type) { + console.log('🔐 Processando confirmação de email...'); + + const { data, error } = await supabase.auth.verifyOtp({ + token_hash, + type: type as any, + }); + + if (error) { + console.error('❌ Erro na confirmação:', error); + setStatus('error'); + setMessage('Erro ao confirmar email. O link pode ter expirado.'); + toast.error("Erro na confirmação", { + description: "O link de confirmação pode ter expirado. Tente fazer login novamente.", + }); + } else { + console.log('✅ Email confirmado com sucesso:', data); + setStatus('success'); + setMessage('Email confirmado com sucesso! Você já pode fazer login.'); + toast.success("Email confirmado!", { + description: "Sua conta foi ativada com sucesso. Redirecionando...", + }); + + // Redirecionar para login após 3 segundos + setTimeout(() => { + navigate('/auth', { + state: { + showSuccessMessage: true, + message: "✅ Email confirmado! Agora você pode fazer login com suas credenciais." + } + }); + }, 3000); + } + } else { + setStatus('error'); + setMessage('Link de confirmação inválido.'); + } + } catch (error) { + console.error('❌ Erro geral na confirmação:', error); + setStatus('error'); + setMessage('Ocorreu um erro inesperado.'); + } + }; + + handleEmailConfirmation(); + }, [searchParams, navigate]); + + const handleBackToLogin = () => { + navigate('/auth'); + }; + + return ( +
+ + + Confirmação de Email + + {status === 'loading' && "Processando confirmação do seu email..."} + {status === 'success' && "Email confirmado com sucesso!"} + {status === 'error' && "Problema na confirmação"} + + + +
+ {status === 'loading' && ( + <> + +

+ Aguarde enquanto confirmamos seu email... +

+ + )} + + {status === 'success' && ( + <> + +

+ {message} +

+

+ Você será redirecionado automaticamente em alguns segundos... +

+ + )} + + {status === 'error' && ( + <> + +

+ {message} +

+ + + )} +
+
+
+
+ ); +}; + +export default EmailConfirmation;