nuxt-auth-kit
Module Nuxt d'authentification clé-en-main pour API Laravel. Installez-le une fois — connexion, inscription, profil, mots de passe, rôles et permissions sont prêts à l'emploi.
Fonctionnalités
Tout ce dont vous avez besoin, sans rien configurer manuellement.
hasRole() et
hasPermission() disponibles partout.
auth, guest, role dans
definePageMeta — aucun import.
ui sur chaque composant — couleurs, rounded,
boutons, layout.
PhoneInput avec sélecteur de pays,
drapeaux, formatage auto.
Installation
Compatible Nuxt 3.x et Nuxt 4.x. Requiert
@nuxt/ui pour les styles.
npm install nuxt-auth-kit
pnpm add nuxt-auth-kit
yarn add nuxt-auth-kit
Configuration
Ajoutez le module dans nuxt.config.ts et pointez sur
votre API Laravel.
export default defineNuxtConfig({ modules: [ 'nuxt-auth-kit', '@nuxt/ui' // requis pour les styles Tailwind ], nuxtAuthKit: { apiBase: process.env.NUXT_PUBLIC_API_BASE || 'http://localhost:8000', endpoints: { login: '/api/auth/login', register: '/api/auth/register', logout: '/api/auth/logout', me: '/api/auth/me', updateProfile: '/api/profile', updatePassword: '/api/profile/password', forgotPassword: '/api/auth/forgot-password', resetPassword: '/api/auth/reset-password' }, redirects: { login: '/auth/login', home: '/', afterLogout: '/auth/login' }, tokenCookieName: 'auth_token', rbac: { superAdminRole: 'super-admin', defaultUserRole: 'user' } } })
NUXT_PUBLIC_API_BASE=https://api.monprojet.com
Auto-import — useAuth,
useAuthStore, tous les composants et les middlewares
sont auto-importés. Aucun import nécessaire dans vos
fichiers.
Changelog
AuthLayout
Layout split-screen — formulaire à gauche, témoignage + illustration à droite.
| Prop | Type | Défaut | Description |
|---|---|---|---|
app-name |
string |
'App' |
Nom du logo |
quote |
object |
— | { text, author, location, avatar? } |
Slots : #logo (remplace le logo),
#illustration (remplace le SVG de droite).
<template> <AuthLayout :quote="quote"> <!-- formulaire ici --> </AuthLayout> </template> <script setup lang="ts"> // Aucun import — AuthLayout est auto-importé const quote = { text: 'Une expérience fluide et agréable. La plateforme rend tout si simple.', author: '3S Tech Group', location: 'Dakar, Sénégal', appName: '3S-Auth', } </script>
AuthLoginForm
Formulaire de connexion avec email/mot de passe, social login optionnel et sélecteur de rôle.
| Prop | Type | Défaut | Description |
|---|---|---|---|
title |
string |
'Connexion' |
Titre du formulaire |
subtitle |
string |
— | Sous-titre |
show-social |
boolean |
false |
Boutons Google / Apple |
roles |
Array<{value,label}> |
[] |
Sélecteur de rôle |
| Événement | Description |
|---|---|
@forgot-password |
Clic "Mot de passe oublié ?" |
@register |
Clic "S'inscrire" |
@google-login |
Clic bouton Google |
@apple-login |
Clic bouton Apple |
@success |
Connexion réussie |
<template> <AuthLayout :quote="quote"> <AuthLoginForm :show-social="true" @forgot-password="navigateTo('/auth/forgot-password')" @register="navigateTo('/auth/register')" /> </AuthLayout> </template> <script setup lang="ts"> // Aucun import — tout est auto-importé definePageMeta({ middleware: 'guest' }) const quote = { text: 'Une expérience fluide et agréable.', author: '3S Tech Group', location: 'Dakar' } </script>
AuthRegisterForm
Inscription avec prénom, nom, email, téléphone (optionnel), mot de
passe et confirmation. Les champs peuvent être masqués via la prop
except.
| Prop | Type | Défaut | Description |
|---|---|---|---|
title |
string |
'Créer un compte' |
Titre du formulaire |
subtitle |
string |
'Rejoignez-nous dès aujourd'hui.' |
Sous-titre |
except |
('password' | 'phone')[] |
[] |
Champs à masquer et exclure de la validation |
Le champ phone est
masqué par défaut. Il s'affiche dès qu'il n'est
pas dans except. Le champ password est
visible par défaut — passez
:except="['password']" pour le masquer (ex :
inscription via magic link).
<template> <AuthLayout> <!-- Défaut — mot de passe requis, sans téléphone --> <AuthRegisterForm @login="navigateTo('/auth/login')" /> <!-- Sans mot de passe (ex: magic link) --> <AuthRegisterForm :except="['password']" @login="navigateTo('/auth/login')" /> <!-- Avec téléphone, sans mot de passe --> <AuthRegisterForm :except="['password']" @login="navigateTo('/auth/login')" /> <!-- Sans téléphone ni mot de passe --> <AuthRegisterForm :except="['password', 'phone']" @login="navigateTo('/auth/login')" /> </AuthLayout> </template> <script setup lang="ts"> definePageMeta({ middleware: 'guest' }) </script>
AuthForgotPasswordForm
Envoi de l'email de réinitialisation. Affiche un état de succès après envoi.
<template> <AuthLayout> <AuthForgotPasswordForm @back-to-login="navigateTo('/auth/login')" /> </AuthLayout> </template> <script setup lang="ts"> definePageMeta({ middleware: 'guest' }) </script>
AuthResetPasswordForm
Lit automatiquement ?token= et
?email= depuis l'URL de réinitialisation.
<template> <AuthLayout> <AuthResetPasswordForm @back-to-login="navigateTo('/auth/login')" /> </AuthLayout> </template> <script setup lang="ts"> // Lit ?token=xxx&email=xxx depuis useRoute() automatiquement definePageMeta({ middleware: 'guest' }) </script>
L'email Laravel doit rediriger vers
/auth/reset-password?token=TOKEN&email=EMAIL. Configurez dans config/auth.php →
passwords.users.
ProfileUpdateForm
Mise à jour du profil connecté : prénom, nom, email, téléphone et
avatar. Les champs peuvent être masqués via la prop
except.
| Prop | Type | Défaut | Description |
|---|---|---|---|
title |
string |
'Informations du profil' |
Titre de la section |
show-avatar |
boolean |
false |
Affiche la section avatar avec prévisualisation et initiales |
except |
('first_name' | 'last_name' | 'email' | 'phone')[]
|
[] |
Champs à masquer et exclure de la validation |
Le champ phone est
masqué par défaut (absent du formulaire
original). Il s'affiche dès qu'il n'est pas dans
except. Les champs first_name,
last_name et email sont visibles par
défaut.
Slot #extra-fields pour ajouter des champs
supplémentaires. Accès à l'objet form via la prop de
slot.
<template> <div class="max-w-2xl mx-auto py-10 px-4 space-y-10"> <!-- Défaut — prénom, nom, email visibles --> <ProfileUpdateForm title="Mon profil" :show-avatar="true" @success="onProfileSaved" /> <!-- Avec téléphone, sans email --> <ProfileUpdateForm :except="['email']" @success="onProfileSaved" /> <!-- Nom uniquement --> <ProfileUpdateForm :except="['email', 'phone']" @success="onProfileSaved" /> </div> </template> <script setup lang="ts"> definePageMeta({ middleware: 'auth' }) function onProfileSaved() { // ex: afficher une notification toast } </script>
ProfileUpdatePasswordForm
Changement du mot de passe. Requiert l'ancien mot de passe.
| Prop | Type | Défaut |
|---|---|---|
title |
string |
'Changer le mot de passe' |
<template> <div class="max-w-2xl mx-auto py-10 px-4 space-y-10"> <ProfileUpdatePasswordForm title="Changer le mot de passe" /> </div> </template> <script setup lang="ts"> definePageMeta({ middleware: 'auth' }) </script>
useAuth()
Composable principal — auto-importé, aucun
import nécessaire dans vos fichiers.
État réactif
null si déconnecté.
true si un utilisateur est connecté avec un token
valide.
isAuthenticated.
true pendant une requête en cours.
Actions
redirects.home en cas de
succès.
redirects.afterLogout.
/api/auth/me. Appelé
automatiquement au boot via le plugin.
File pour l'avatar
(envoi en multipart/form-data auto).
current_password,
new_password, password_confirmation.
{ token, email, password, password_confirmation }.
<script setup lang="ts"> // useAuth est auto-importé — aucun import nécessaire const { user, isAuthenticated, logout, loading } = useAuth() async function handleLogout() { await logout() // redirige automatiquement } </script> <template> <div> <span v-if="isAuthenticated">Bonjour, {{ user?.name }}</span> <button @click="handleLogout" :disabled="loading">Déconnexion</button> </div> </template>
Rôles & Permissions
hasRole() et hasPermission() accessibles
via useAuth() — auto-importé.
const { hasRole, hasPermission } = useAuth() // Rôle unique if (hasRole('admin')) { /* ... */ } // Plusieurs rôles — au moins un suffit if (hasRole(['admin', 'manager'])) { /* ... */ } // Permission unique if (hasPermission('edit-posts')) { /* ... */ }
<template> <AdminPanel v-if="hasRole('admin')" /> <button v-if="hasPermission('create-post')"> Créer un article </button> </template> <script setup lang="ts"> const { hasRole, hasPermission } = useAuth() </script>
Le rôle super-admin (configurable via
rbac.superAdminRole) bypasse toutes les vérifications
dans le middleware role.
Middlewares
Trois middlewares nommés, utilisables directement dans
definePageMeta. Aucun import.
/auth/login si non connecté. Conserve
l'URL dans ?redirect=.
/ si déjà connecté. À utiliser sur
login, register, etc.
meta.roles.
// Page connectés uniquement definePageMeta({ middleware: 'auth' }) // Page visiteurs uniquement definePageMeta({ middleware: 'guest' }) // Page avec rôle requis definePageMeta({ middleware: 'role', roles: ['admin', 'manager'] }) // Connecté ET rôle requis definePageMeta({ middleware: ['auth', 'role'], roles: ['admin'] })
API Laravel attendue
Tous les endpoints sont personnalisables via
nuxtAuthKit.endpoints.
| Méthode | Route | Auth | Réponse | Description |
|---|---|---|---|---|
| POST | /api/auth/login | — | { user, token } | Connexion |
| POST | /api/auth/register | — | { user, token } | Inscription |
| POST | /api/auth/logout | ✅ | { message } | Déconnexion |
| GET | /api/auth/me | ✅ | { user } | Utilisateur connecté |
| PUT | /api/profile | ✅ | { user } | Mise à jour profil |
| PUT | /api/profile/password | ✅ | { message } | Changement mdp |
| POST | /api/auth/forgot-password | — | { message } | Email reset |
| POST | /api/auth/reset-password | — | { message } | Reset avec token |
Le champ user doit contenir au minimum
id, name, email. Les champs
roles et permissions (tableaux de
strings) sont optionnels pour activer le RBAC.
Exemple avec Laravel Sanctum
use App\Http\Controllers\AuthController; Route::prefix('auth')->group(function () { Route::post('login', [AuthController::class, 'login']); Route::post('register', [AuthController::class, 'register']); Route::post('forgot-password', [AuthController::class, 'forgotPassword']); Route::post('reset-password', [AuthController::class, 'resetPassword']); Route::middleware('auth:sanctum')->group(function () { Route::post('logout', [AuthController::class, 'logout']); Route::get('me', [AuthController::class, 'me']); Route::put('profile', [AuthController::class, 'updateProfile']); Route::put('password', [AuthController::class, 'updatePassword']); }); });
public function me(Request $request): JsonResponse { return response()->json([ 'user' => $request->user()->load('roles', 'permissions'), ]); }
Prop ui
Chaque composant accepte une prop
ui?: Partial<FormTheme> pour surcharger
partiellement les styles — couleurs, arrondis, variantes de
boutons, couleurs du layout.
Formulaires auth & profil
<AuthLoginForm :ui="{ inputRounded: 'rounded-lg', btnColor: 'primary', btnRounded: 'rounded-lg', titleColor: 'text-slate-900', accentColor: 'text-blue-600', }" />
AuthLayout
<AuthLayout :quote="quote" :ui="{ layoutPageColor: '#1e293b', layoutTextColor: '#f8fafc', layoutTaglineColor: 'rgba(248,250,252,0.7)', }" />
Tokens disponibles
| Token | Défaut | Description |
|---|---|---|
inputRounded |
'rounded-full' |
Border-radius des inputs |
color |
'neutral' |
Couleur Nuxt UI des inputs |
btnRounded |
'rounded-full' |
Border-radius du bouton principal |
btnColor |
'neutral' |
Couleur Nuxt UI du bouton principal |
btnVariant |
'solid' |
Variante du bouton principal |
btnSecondaryColor |
'secondary' |
Couleur des boutons secondaires |
btnSecondaryVariant |
'subtle' |
Variante des boutons secondaires |
btnSecondaryRounded |
'rounded-full' |
Border-radius des boutons secondaires |
titleColor |
'text-[#1a2e1a]' |
Couleur du titre (classe Tailwind) |
subtitleColor |
'text-[#6b7c6b]' |
Couleur du sous-titre |
accentColor |
'text-[#1B4332]' |
Liens & couleur d'accent |
roleRingColor |
'ring-[#1B4332]' |
Ring du sélecteur de rôle actif |
layoutPageColor |
'#eeeee6' |
Fond du panneau gauche (valeur CSS) |
layoutTextColor |
'#ffffff' |
Couleur appName panneau droit |
layoutTaglineColor |
'rgba(255,255,255,0.75)' |
Couleur tagline panneau droit |
PhoneInput
Composant de saisie de numéro de téléphone — sélecteur de pays
avec drapeaux, indicatifs, formatage AsYouType,
validation E.164, détection locale automatique.
<PhoneInput v-model="form.phone" v-model:country-code="form.phoneCountry" :preferred-countries="['SN', 'FR', 'CI', 'MA', 'CM']" :use-browser-locale="true" :ui="{ inputRounded: 'rounded-xl' }" @data="onPhoneData" />
Payload de l'événement @data
interface PhoneInputData { e164: string | null // ex: '+221771234567' — à stocker en BDD countryCode: string | null // ex: 'SN' formatted: string // ex: '77 123 45 67' — format national isValid: boolean }
Props
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
string |
'' |
v-model (E.164 ou national) |
countryCode |
string |
— | v-model:countryCode |
defaultCountry |
string |
— | Pays initial (ISO 3166) |
preferredCountries |
string[] |
[] |
Pays épinglés en haut |
onlyCountries |
string[] |
[] |
Restreindre à ces pays |
ignoredCountries |
string[] |
[] |
Exclure ces pays |
useBrowserLocale |
boolean |
true |
Détecter depuis le navigateur |
placeholder |
string |
— | Placeholder personnalisé |
disabled |
boolean |
false |
Désactiver le champ |
error |
string | boolean |
— | Message d'erreur externe |
ui |
Partial<FormTheme> |
{} |
Surcharge de style |
Types TypeScript
Exportés depuis nuxt-auth-kit. À importer uniquement
si vous en avez besoin explicitement.
import type { AuthUser, // structure de l'utilisateur LoginCredentials, // { email, password, remember? } RegisterData, // { name, email, password, password_confirmation } UpdateProfileData, // { name?, email?, avatar? } UpdatePasswordData, // { current_password, new_password, new_password_confirmation } ForgotPasswordData, // { email } ResetPasswordData, // { token, email, password, password_confirmation } AuthResponse, // { user, token } ApiError, // { message, errors? } ModuleOptions // config nuxtAuthKit } from 'nuxt-auth-kit' // Structure minimale de AuthUser interface AuthUser { id: number | string name: string email: string avatar?: string roles?: string[] permissions?: string[] [key: string]: unknown }
Architecture
nuxt-auth-kit/ ├── build.config.ts # config unbuild ├── package.json # v1.2.0 └── src/ ├── module.ts # point d'entrée du module Nuxt └── runtime/ ├── types/index.ts # types TypeScript ├── stores/auth.ts # Pinia store ├── composables/useAuth.ts # composable principal ├── plugins/auth.ts # restauration session au boot ├── middleware/ │ ├── auth.ts │ ├── guest.ts │ └── role.ts └── components/ ├── auth/ │ ├── AuthLayout.vue │ ├── LoginForm.vue │ ├── RegisterForm.vue │ ├── ForgotPasswordForm.vue │ └── ResetPasswordForm.vue ├── profile/ │ ├── UpdateProfileForm.vue │ └── UpdatePasswordForm.vue └── ui/ └── PhoneInput.vue # 🆕 v1.2.0 # composables/useFormTheme.ts 🆕 v1.2.0