Admin Auth (Vite)
Different from backend — cookie + localStorage + React Context. Different from Next.js — React Router guards, not middleware.
Storage
| What | Where |
|---|---|
| JWT | Cookie token (7-day, SameSite=Lax) |
| User profile | localStorage key auth_user |
AuthContext
const { user, isAuthenticated, initialized, login, logout, hasRole } = useAuth();
// Wait for initialized before rendering protected routes
// hasRole(['ADMIN', 'EDITOR']) checks user.role
Files: features/auth/context/AuthContext.tsx
Login Flow
POST /auth/login→ extract token (flexible shapes:accessToken,token,data.token)setCookie('token', token)GET /auth/get-profile→ set user in context + localStorage- Redirect to
?redirect=param or/dashboard
Axios Interceptor
// lib/api.ts
api.interceptors.request.use((config) => {
const token = getCookie('token');
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
api.interceptors.response.use(null, (error) => {
if (error?.response?.status === 401) {
deleteCookie('token');
localStorage.removeItem('auth_user');
window.location.href = `/login?redirect=${encodeURIComponent(pathname)}`;
}
return Promise.reject(error);
});
Route Guards
// RequireAuth — wait for init, redirect /login
{ path: '/', element: <RequireAuth><App /></RequireAuth>, children: [...] }
// RequireRole — per route
{
path: '/dashboard/categories',
element: <RequireRole roles={['ADMIN', 'EDITOR']}><CategoryListPage /></RequireRole>,
}
Unauthorized role → redirect /dashboard (not login).
Roles
ADMIN, ACCOUNTANT, EDITOR, ORDER_HANDLER, MARKETER, ORDER_DRIVER
Sidebar + Route Double-Gate
Sidebar hides links by roles in config. Routes enforce RequireRole as real security boundary.
Order Handler Verification (domain-specific)
OrderHandlerContext — daily password verify for handler/driver IDs in localStorage.
Logout
Clear cookie + localStorage → hard redirect /login.
Do not implement JWT signing or backend guards here — only consume token.