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

FeatureSkill
Login formPOST token, store in Pinia
Protected routesbeforeEach + meta.requiresAuth
Layout shellNested routes, named RouterView or slot layout
LogoutClear token, redirect login
401 handlingInterceptor or composable redirect

Auth Store

src/stores/auth.ts:

Mock login for local dev (no backend):

typescript
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 /dashboard redirects 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 Authorization header
  • 401 clears session and returns to login

Extensions

  • Role-based meta.roles guard
  • 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.

Frontend sends credentials: 'include'; backend sets Set-Cookie—no manual header.

Connect to FastAPI?

Match login path and response shape from Authentication and JWT.

Next optional project?

Multi-step form.