Protected Routes

ProtectedRoute Component

With React Router 6, a protected route is a component that renders <Outlet /> (the child page) if the user is allowed, or redirects otherwise.

// src/auth/ProtectedRoute.jsx
import { Navigate, Outlet, useLocation } from 'react-router-dom';
import { useAuth } from './AuthContext';

export default function ProtectedRoute({ roles }) {
  const { user } = useAuth();
  const location = useLocation();

  if (!user) {
    // remember where the user wanted to go
    return <Navigate to="/login" replace state={{ from: location }} />;
  }
  if (roles && !roles.some((r) => user.roles.includes(r))) {
    return <Navigate to="/forbidden" replace />;
  }
  return <Outlet />;
}

Routes

// src/App.jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { AuthProvider } from './auth/AuthContext';
import ProtectedRoute from './auth/ProtectedRoute';
import HomePage from './pages/HomePage';
import LoginPage from './pages/LoginPage';
import OrdersPage from './pages/OrdersPage';
import SellerDashboard from './pages/SellerDashboard';
import AdminPage from './pages/AdminPage';
import ForbiddenPage from './pages/ForbiddenPage';

export default function App() {
  return (
    <AuthProvider>
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/login" element={<LoginPage />} />
          <Route path="/forbidden" element={<ForbiddenPage />} />

          <Route element={<ProtectedRoute />}>
            <Route path="/orders" element={<OrdersPage />} />
          </Route>
          <Route element={<ProtectedRoute roles={['ROLE_SELLER', 'ROLE_ADMIN']} />}>
            <Route path="/seller" element={<SellerDashboard />} />
          </Route>
          <Route element={<ProtectedRoute roles={['ROLE_ADMIN']} />}>
            <Route path="/admin" element={<AdminPage />} />
          </Route>
        </Routes>
      </BrowserRouter>
    </AuthProvider>
  );
}

Frontend Checks Are Not Security

Note: Anyone can open DevTools and edit shopease_user in localStorage to add ROLE_ADMIN. They would see the admin page, but every API call it makes would get 403, because the server checks the signed token. Route guards are for user experience; the real security is always on the server.