Project: Admin Dashboard With Auth
Introduction
This project builds a mini admin panel: login page, JWT-protected routes, sidebar layout with nested routes, and API calls with Authorization: Bearer. Use a mock login or connect to FastAPI JWT or Django API auth.
Prerequisites
Goals
| Feature | Skill |
|---|---|
| Login form | POST token, store in Pinia |
| Protected routes | beforeEach + meta.requiresAuth |
| Layout shell | Nested routes, named RouterView or slot layout |
| Logout | Clear token, redirect login |
| 401 handling | Interceptor or composable redirect |
Auth Store
src/stores/auth.ts:
Mock login for local dev (no backend):
async function login(email: string, password: string) {
if (email === "admin@example.com" && password === "demo") {
setToken("mock-jwt-token");
user.value = { email };
return;
}
throw new Error("Invalid credentials");
}Authenticated Fetch
src/api/client.ts:
Warning
Do not store JWT in localStorage for high-security apps without understanding XSS risk. httpOnly cookies need backend cookie auth—see FastAPI JWT chapter for trade-offs.
Router Layout
src/router/index.ts:
beforeEach guard—use Pinia (must be after app.use(pinia)):
AdminLayout
src/layouts/AdminLayout.vue:
LoginView
DashboardView (Protected API)
Deliverables
- Unauthenticated visit to
/dashboardredirects to login with?redirect= - Successful login lands on dashboard (or redirect target)
- Sidebar layout persists across nested routes
- Logout clears token and blocks protected routes
- Protected API call sends
Authorizationheader - 401 clears session and returns to login
Extensions
- Role-based
meta.rolesguard - Refresh token flow (advanced—backend support required)
- Breadcrumb from matched routes
- Dark mode toggle in layout (Pinia + CSS variables)
FAQ
Pinia in router before app mount?
Create router after Pinia is installed, or read token from localStorage in guard then sync store on app init.
Why nested routes?
Shared layout without re-mounting sidebar on every navigation.
Cookie auth instead of Bearer?
Frontend sends credentials: 'include'; backend sets Set-Cookie—no manual header.
Connect to FastAPI?
Match login path and response shape from Authentication and JWT.