Ensure authenticated access to dashboard
Implement authentication check for dashboard route, redirecting unauthenticated users to the authentication page.
This commit is contained in:
parent
60ee8bb62e
commit
2f66c2ddcd
56
src/App.tsx
56
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,33 +13,58 @@ 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="/auth"
|
||||
element={
|
||||
<AuthGuard>
|
||||
<Auth />
|
||||
</AuthGuard>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/dashboard"
|
||||
element={
|
||||
@ -52,9 +77,18 @@ const App = () => (
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</BrowserRouter>
|
||||
</TooltipProvider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const App = () => (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<BrowserRouter>
|
||||
<AuthProvider>
|
||||
<AppContent />
|
||||
</AuthProvider>
|
||||
</BrowserRouter>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user