# Frontend Vite Auth

> Vite admin authentication: cookie JWT, AuthContext, RequireAuth, RequireRole RBAC, axios 401 interceptor, login flow. Use when implementing login, protected routes, role guards, or auth in Vite React admin dashboards.

- Skill: `xmuhameed/frontend-vite-auth` (Agent Skill)
- Install (CLI): `npx skillmds@latest add xmuhameed/frontend-vite-auth`
- Raw SKILL.md: https://api.skillmd.com/api/skills/xmuhameed/frontend-vite-auth/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: xmuhameed (https://skillmd.com/u/xmuhameed)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/xmuhameed/frontend-vite-auth

---


# 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

```typescript
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

1. `POST /auth/login` → extract token (flexible shapes: `accessToken`, `token`, `data.token`)
2. `setCookie('token', token)`
3. `GET /auth/get-profile` → set user in context + localStorage
4. Redirect to `?redirect=` param or `/dashboard`

## Axios Interceptor

```typescript
// 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

```typescript
// 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.

