| name | spa-auth-development |
| description | Activate when working on SPA authentication flow, Sanctum cookie-based auth, Vue Router guards, auth store, login/register/password reset pages, or CORS/session configuration. Covers the full auth lifecycle from CSRF cookie to protected API calls. |
| license | MIT |
| metadata | {"author":"project"} |
SPA Authentication Development
This project uses a decoupled SPA architecture where Vue handles all routing and Laravel serves as a JSON API with Sanctum cookie-based authentication.
Auth Flow
- Vue app calls
GET /sanctum/csrf-cookie to get XSRF token
- User submits login form →
POST /login (Fortify handles this)
- Laravel returns session cookie + user data
- All subsequent API calls include the session cookie automatically (
withCredentials: true)
- Protected routes use
auth:sanctum middleware
Key Files
Backend
config/sanctum.php — Stateful domains, guards, CSRF middleware
config/cors.php — CORS paths (api/*, login, logout, register, etc.) with supports_credentials: true
config/fortify.php — Auth features (registration, password reset, email verification)
app/Providers/FortifyServiceProvider.php — Binds Fortify actions and custom LoginResponse
app/Http/Responses/LoginResponse.php — Returns JSON with user data for SPA
app/Http/Middleware/ApplyLocale.php — Sets locale from X-Locale header
bootstrap/app.php — statefulApi() middleware, apply_locale alias
Frontend
stores/auth.js — Pinia store with login, register, logout, getCurrentUser
services/AuthService.ts — API calls for auth endpoints (login, register, forgot/reset password, etc.)
router/index.js — Navigation guards checking requiresAuth and requiresAbility route meta
plugins/axios.js — Axios configured with withCredentials, X-Locale header, base URL from window.AppConfig
CORS Configuration
For the SPA to work, these .env values must match:
APP_URL=http://localhost:8000
SANCTUM_STATEFUL_DOMAINS=localhost:8000
SESSION_DOMAIN=localhost
Route Guards
Routes use meta fields for auth:
requiresAuth: true — Redirects to login if not authenticated
requiresAbility: 'ability_name' — Checks user abilities via Bouncer
isPublicAuthPage: true — Redirects to dashboard if already authenticated
isOwner: true — Owner-only routes
Authorization
Uses Silber Bouncer with abilities:
- Abilities defined in
resources/app/stub/abilities.js (CREATE_USER, EDIT_USER, etc.)
- Checked in Vue Router guards and sidebar menu visibility
- Backend uses
Gate::authorize() in controllers
- Roles seeded in
database/seeders/BouncerSeeder.php