Ensure authenticated access to dashboard

Implement authentication check for dashboard route, redirecting unauthenticated users to the authentication page.
This commit is contained in:
gpt-engineer-app[bot] 2025-03-10 09:26:18 +00:00
parent 60ee8bb62e
commit 2f66c2ddcd
3 changed files with 90 additions and 37 deletions

View File

@ -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 <div className="min-h-screen flex items-center justify-center">Loading...</div>;
}
if (!user) {
return <Navigate to="/auth" />;
if (!isAuthenticated) {
// Redirect to the auth page but save the current location they were trying to access
return <Navigate to="/auth" state={{ from: location }} replace />;
}
return <>{children}</>;
};
const App = () => (
<QueryClientProvider client={queryClient}>
<AuthProvider>
// Authentication guard to prevent authenticated users from accessing the auth page
const AuthGuard = ({ children }: { children: React.ReactNode }) => {
const { isAuthenticated, loading } = useAuth();
if (loading) {
return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
}
if (isAuthenticated) {
// If already logged in, redirect to dashboard
return <Navigate to="/dashboard" replace />;
}
return <>{children}</>;
};
// App needs to be wrapped with BrowserRouter to use the AuthProvider with routing capabilities
const AppContent = () => {
return (
<>
<TooltipProvider>
<Toaster />
<Sonner />
<BrowserRouter>
<Header />
<div className="pt-16 sm:pt-20">
<Routes>
<Route path="/" element={<Index />} />
<Route path="/auth" element={<Auth />} />
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
<Route path="*" element={<NotFound />} />
</Routes>
</div>
</BrowserRouter>
<Header />
<div className="pt-16 sm:pt-20">
<Routes>
<Route path="/" element={<Index />} />
<Route
path="/auth"
element={
<AuthGuard>
<Auth />
</AuthGuard>
}
/>
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
<Route path="*" element={<NotFound />} />
</Routes>
</div>
</TooltipProvider>
</AuthProvider>
</>
);
};
const App = () => (
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<AuthProvider>
<AppContent />
</AuthProvider>
</BrowserRouter>
</QueryClientProvider>
);

View File

@ -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<void>;
signUp: (email: string, password: string, fullName: string) => Promise<void>;
signOut: () => Promise<void>;
isAuthenticated: boolean;
};
const AuthContext = createContext<AuthContextType | undefined>(undefined);
@ -21,13 +22,17 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
const [user, setUser] = useState<User | null>(null);
const [profile, setProfile] = useState<any | null>(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}

View File

@ -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<string | null>(null);
const [activeTab, setActiveTab] = useState('login');
// If user is already logged in, redirect to dashboard
if (user) {
return <Navigate to="/dashboard" />;
}
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