From 2f66c2ddcd2c168724b5c52313f6b8f524cee3db Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 09:26:18 +0000 Subject: [PATCH] Ensure authenticated access to dashboard Implement authentication check for dashboard route, redirecting unauthenticated users to the authentication page. --- src/App.tsx | 90 +++++++++++++++++++++++++----------- src/contexts/AuthContext.tsx | 21 ++++++++- src/pages/Auth.tsx | 16 ++++--- 3 files changed, 90 insertions(+), 37 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 0cfa1ed..31cab99 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,7 +3,7 @@ import { Toaster } from "@/components/ui/toaster"; import { Toaster as Sonner } from "@/components/ui/sonner"; import { TooltipProvider } from "@/components/ui/tooltip"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; +import { BrowserRouter, Routes, Route, Navigate, useLocation } from "react-router-dom"; import { AuthProvider, useAuth } from "@/contexts/AuthContext"; import Index from "./pages/Index"; import Auth from "./pages/Auth"; @@ -13,48 +13,82 @@ import Header from "./components/Header"; const queryClient = new QueryClient(); -// Protected route component +// Enhanced Protected route component that preserves the intended destination const ProtectedRoute = ({ children }: { children: React.ReactNode }) => { - const { user, loading } = useAuth(); + const { user, loading, isAuthenticated } = useAuth(); + const location = useLocation(); if (loading) { return
Loading...
; } - if (!user) { - return ; + if (!isAuthenticated) { + // Redirect to the auth page but save the current location they were trying to access + return ; } return <>{children}; }; -const App = () => ( - - +// Authentication guard to prevent authenticated users from accessing the auth page +const AuthGuard = ({ children }: { children: React.ReactNode }) => { + const { isAuthenticated, loading } = useAuth(); + + if (loading) { + return
Loading...
; + } + + if (isAuthenticated) { + // If already logged in, redirect to dashboard + return ; + } + + return <>{children}; +}; + +// App needs to be wrapped with BrowserRouter to use the AuthProvider with routing capabilities +const AppContent = () => { + return ( + <> - -
-
- - } /> - } /> - - - - } - /> - {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} - } /> - -
- +
+
+ + } /> + + + + } + /> + + + + } + /> + {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} + } /> + +
- + + ); +}; + +const App = () => ( + + + + + + ); diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index 5464709..3120e2f 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -1,8 +1,8 @@ - import { createContext, useContext, useState, useEffect, ReactNode } from 'react'; import { supabase } from '@/integrations/supabase/client'; import { Session, User } from '@supabase/supabase-js'; import { useToast } from '@/hooks/use-toast'; +import { useNavigate, useLocation } from 'react-router-dom'; type AuthContextType = { session: Session | null; @@ -12,6 +12,7 @@ type AuthContextType = { signIn: (email: string, password: string) => Promise; signUp: (email: string, password: string, fullName: string) => Promise; signOut: () => Promise; + isAuthenticated: boolean; }; const AuthContext = createContext(undefined); @@ -21,13 +22,17 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { const [user, setUser] = useState(null); const [profile, setProfile] = useState(null); const [loading, setLoading] = useState(true); + const [isAuthenticated, setIsAuthenticated] = useState(false); const { toast } = useToast(); + const navigate = useNavigate(); + const location = useLocation(); useEffect(() => { // Get the initial session supabase.auth.getSession().then(({ data: { session } }) => { setSession(session); setUser(session?.user ?? null); + setIsAuthenticated(!!session?.user); if (session?.user) { fetchProfile(session.user.id); } @@ -39,10 +44,15 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { (_event, session) => { setSession(session); setUser(session?.user ?? null); + setIsAuthenticated(!!session?.user); if (session?.user) { fetchProfile(session.user.id); } else { setProfile(null); + // If on a protected route and logged out, redirect to auth + if (location.pathname === '/dashboard') { + navigate('/auth'); + } } setLoading(false); } @@ -51,7 +61,7 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { return () => { subscription.unsubscribe(); }; - }, []); + }, [navigate, location.pathname]); const fetchProfile = async (userId: string) => { try { @@ -83,6 +93,9 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { title: "Success!", description: "You are now signed in.", }); + + // Redirect to dashboard on successful sign in + navigate('/dashboard'); } catch (error: any) { toast({ title: "Error signing in", @@ -137,6 +150,9 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { title: "Signed out", description: "You have been successfully signed out.", }); + + // Always redirect to home page after sign out + navigate('/'); } catch (error: any) { toast({ title: "Error signing out", @@ -159,6 +175,7 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { signIn, signUp, signOut, + isAuthenticated, }} > {children} diff --git a/src/pages/Auth.tsx b/src/pages/Auth.tsx index 9831d28..a5309dc 100644 --- a/src/pages/Auth.tsx +++ b/src/pages/Auth.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; -import { Navigate } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import { useAuth } from '@/contexts/AuthContext'; import { ShieldCheck, Mail, Lock, User, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; @@ -10,23 +10,25 @@ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; const Auth = () => { - const { user, signIn, signUp, loading } = useAuth(); + const { signIn, signUp, loading } = useAuth(); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [fullName, setFullName] = useState(''); const [authError, setAuthError] = useState(null); const [activeTab, setActiveTab] = useState('login'); - - // If user is already logged in, redirect to dashboard - if (user) { - return ; - } + + const location = useLocation(); + const navigate = useNavigate(); + + // Get the intended destination from the location state + const from = (location.state as any)?.from?.pathname || '/dashboard'; const handleSignIn = async (e: React.FormEvent) => { e.preventDefault(); setAuthError(null); try { await signIn(email, password); + // Redirect will be handled in the AuthContext after successful sign in } catch (error: any) { console.error('Sign in error:', error); // Error is already handled by the toast in AuthContext