1. Accueil
  2. Articles
5 min de lecture
0 vues

Comment sécuriser une SPA Nuxt avec Laravel Sanctum et Fortify

Image d'illustration pour Comment sécuriser une SPA  Nuxt avec Laravel Sanctum et Fortify

#Problématique : Le Piège du Bearer Token en SPA

Séparer le backend (Laravel) et le frontend (Nuxt) pose un défi sécuritaire majeur : comment maintenir une session sans ouvrir une faille ? L'approche classique consiste à utiliser des Bearer Tokens stockés dans le localStorage. C'est simple, mais catastrophique en termes de sécurité : un simple script malveillant (XSS) peut voler ce token et prendre le contrôle du compte utilisateur. Pour une plateforme gérant des paiements ou des données sensibles, c'est un risque inacceptable.

L'alternative robuste est l'authentification SPA par Cookie/Session (Sanctum). Les cookies HttpOnly sont invisibles pour le JavaScript, rendant le vol par XSS impossible. Mais ce mode d'authentification fait naître le cauchemar du développeur : les erreurs CORS (Cross-Origin) et la gestion complexe du CSRF. Faire communiquer Nuxt (localhost:3000) et Laravel (localhost:8000) via des cookies génère souvent des murs d'erreurs 401 Unauthorized.

Comment obtenir la sécurité absolue des cookies Sanctum sans succomber sous la complexité du Cross-Origin ?

#Introduction : La Feuille de Route vers une Auth Zéro Faille

Cet article fournit la solution clé en main. Nous allons démontrer comment combiner **Laravel ** (Sanctum + Fortify) et **Nuxt ** (via le module nuxt-auth-sanctum) pour forger un tunnel d'authentification inviolable et automatisé.

Fini les injections manuelles de tokens et les heures perdues à déboguer les pre-flights CORS. Nous allons configurer Laravel pour qu'il traite Nuxt comme une extension de lui-même (statefulApi), et Nuxt pour qu'il gère le cycle CSRF de façon totalement transparente.

Sommaire:

  1. L'Art de la Guerre CORS : Configuration chirurgicale de Laravel pour accepter les credentials Nuxt exclusivement.
  2. **La Magie Nuxt ** : Automatisation du CSRF et de l'hydratation de l'utilisateur avec nuxt-auth-sanctum.
  3. Des Vues à l'Action : Raccordement direct des formulaires (connexion, inscription) aux endpoints Fortify.
  4. La Forteresse des Routes : Protection des pages via les middlewares sanctum:auth et sanctum:guest.
  5. Le Carnet de Bord des "Gotchas" : Les erreurs 401 et CORS résolues cliniquement.

#Prêt à fermer la porte au XSS et au CSRF ? Plongeons dans l'architecture.


#Vision d'Architecture Globale

La communication s'appuiera sur Laravel Sanctum (Authentification SPA par Cookie/Session) pour une sécurité maximale en milieu Web (protection CSRF automatique) et Laravel Fortify pour la gestion modulaire des flux d'authentification sans surcharge de contrôleurs backend.

Capture d’écran du 2026-05-19 15-53-01.png


#Phases d'Implémentation

#1. Configuration du Backend (Laravel )

Pour que l'authentification SPA basée sur les cookies fonctionne, le backend et le frontend doivent partager le même domaine parent ou s'exécuter avec des configurations CORS méticuleuses.

  • Étape 1.1 : Activer le middleware d'état de Sanctum
    • Le middleware d'état de Sanctum a été configuré au niveau de l'application Laravel (bootstrap/app.php) :
    1// bootstrap/app.php
    2->withMiddleware(function (Middleware $middleware) {
    3 $middleware->statefulApi(); // Active Sanctum pour le SPA (Essentiel !)
    4})
    1// bootstrap/app.php
    2->withMiddleware(function (Middleware $middleware) {
    3 $middleware->statefulApi(); // Active Sanctum pour le SPA (Essentiel !)
    4})
  • Étape 1.2 : Configurer les domaines autorisés
  • Dans config/sanctum.php, renseigner le domaine de votre frontend Nuxt.
1'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost:3000,127.0.0.1:3000')),
1'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost:3000,127.0.0.1:3000')),
  • Étape 1.3 : Autoriser les Cookies & CORS
    • Dans config/cors.php, le support des en-têtes d'identification a été activé et les origines ont été bridées :
    1'paths' => ['api/*', 'sanctum/csrf-cookie', 'login', 'logout', 'register', 'forgot-password', 'reset-password'],
    2'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:3000')],
    3'supports_credentials' => true,
    1'paths' => ['api/*', 'sanctum/csrf-cookie', 'login', 'logout', 'register', 'forgot-password', 'reset-password'],
    2'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:3000')],
    3'supports_credentials' => true,

#2. Configuration du Frontend (Nuxt )

Nous avons installé et configuré le module officiel nuxt-auth-sanctum pour gérer l'interception automatique d'ofetch et le pré-vol CSRF.

  • Étape 2.1 : Installation du module
    1npm install nuxt-auth-sanctum
    1npm install nuxt-auth-sanctum
  • Étape 2.2 : Configuration dans nuxt.config.ts
    1export default defineNuxtConfig({
    2 modules: ['nuxt-auth-sanctum'],
    3 sanctum: {
    4 baseUrl: process.env.NUXT_PUBLIC_API_URL || 'http://localhost:8000',
    5 mode: 'cookie',
    6 endpoints: {
    7 csrf: '/sanctum/csrf-cookie',
    8 login: '/login',
    9 logout: '/logout',
    10 user: '/api/user'
    11 },
    12 redirect: {
    13 onLogin: '/dashboard',
    14 onLogout: '/',
    15 onAuthOnly: '/auth/login',
    16 onGuestOnly: '/dashboard'
    17 }
    18 }
    19})
    1export default defineNuxtConfig({
    2 modules: ['nuxt-auth-sanctum'],
    3 sanctum: {
    4 baseUrl: process.env.NUXT_PUBLIC_API_URL || 'http://localhost:8000',
    5 mode: 'cookie',
    6 endpoints: {
    7 csrf: '/sanctum/csrf-cookie',
    8 login: '/login',
    9 logout: '/logout',
    10 user: '/api/user'
    11 },
    12 redirect: {
    13 onLogin: '/dashboard',
    14 onLogout: '/',
    15 onAuthOnly: '/auth/login',
    16 onGuestOnly: '/dashboard'
    17 }
    18 }
    19})
  • Étape 2.3 : Liaison des variables d'environnement (.env)
1NUXT_PUBLIC_API_URL=http://localhost:8000
1NUXT_PUBLIC_API_URL=http://localhost:8000

#3. Connexion des Écrans Riches (login.vue & register.vue)

  • Étape 3.1 : Raccordement du formulaire de connexion
  • Raccordement du hook login dans login.vue :
1const { login } = useSanctumAuth()
2await login(credentials)
1const { login } = useSanctumAuth()
2await login(credentials)
  • Étape 3.2 : Raccordement du formulaire d'inscription
    • Raccordement de l'inscription via useSanctumClient() dans register.vue avec auto-formatage camerounais +237 :
    1const client = useSanctumClient()
    2await client('/register', {
    3 method: 'POST',
    4 body: {
    5 ...form,
    6 phone: `+237 ${form.phone}`
    7 }
    8})
    1const client = useSanctumClient()
    2await client('/register', {
    3 method: 'POST',
    4 body: {
    5 ...form,
    6 phone: `+237 ${form.phone}`
    7 }
    8})

#4. Protection des Routes & États Réactifs

  • Étape 4.1 : Protection des pages du tableau de bord
    • Application du middleware sanctum:auth sur toutes les pages d'administration sous /dashboard (y compris la gestion des destinations et activités).
    1definePageMeta({
    2 middleware: ['sanctum:auth']
    3})
    1definePageMeta({
    2 middleware: ['sanctum:auth']
    3})
  • Étape 4.2 : Restreindre l'accès visiteur (Guest Only)
  • Blocage des utilisateurs connectés sur /auth/login et /auth/register :
1definePageMeta({
2 layout: 'public',
3 middleware: ['sanctum:guest']
4})
1definePageMeta({
2 layout: 'public',
3 middleware: ['sanctum:guest']
4})
  • Étape 4.3 : Barre d'état dynamique de la Navbar
  • Intégration de useSanctumAuth() dans Header.vue :
    • Suppression du lien statique Dashboard dans l'en-tête public.
    • Remplacement dynamique du menu profil par "Tableau de bord", "Paramètres", "Messagerie" et "Se déconnecter" pour les utilisateurs connectés.

#5. Fonctionnalités Avancées (Fortify)

  • Étape 5.1 : Mise à jour du profil utilisateur
    • Créer une page sous /dashboard/profile permettant de mettre à jour le nom et l'email via le endpoint de Fortify : POST /user/profile-information.
  • Étape 5.2 : Double authentification (2FA)
  • (Facultatif) Intégrer l'activation de la 2FA de Fortify (POST /user/two-factor-authentication) avec affichage du QR Code généré côté frontend.

#Gotchas CORS et Middleware résolus lors de l'implémentation :

Symptôme Cause Réelle Résolution Appliquée
Access to fetch blocked by CORS policy Le wildcard '*' était configuré avec supports_credentials = true, ce qui est interdit par la spécification CORS. Publication de config/cors.php et restriction explicite de allowed_origins à l'adresse du frontend Nuxt : http://localhost:3000.
/api/user 401 Unauthorized après connexion Le middleware de gestion de session de Laravel Sanctum (EnsureFrontendRequestsAreStateful) n'était pas activé. Ajout de $middleware->statefulApi() dans bootstrap/app.php pour que Laravel lise la session et les cookies CSRF de Sanctum.
Avertissements Vue Router No match found Des routes et pages fictives de la maquette (ex: /favoris, /logements) sont référencées dans les menus mais pas encore implémentées. Avertissements normaux de développement (non bloquants).