- remove `protectAdmin`/`isAdmin` re-exports from `features/admin/index.js` to avoid top-level `next/headers` import - add `./features/admin/protect` export entry in `package.json` - lazy-import `next/headers` in `router.js` `requireAuth` to defer resolution - update `features/admin/README.md` to document new import paths - translate `features/auth/index.js` comment to French for consistency
Auth
Ce répertoire gère l'authentification : inscription, connexion, sessions, réinitialisation de mot de passe, vérification d'adresse courriel et gestion du profil. Il expose des server actions Next.js, des routes API REST et des composants de pages prêts à l'emploi.
Structure
src/features/auth/
├── index.js barrel serveur
├── actions.js server actions Next.js ('use server')
├── api.js routes API REST (users, roles)
├── auth.js register, login, resetPassword, updateUser, completeAccountSetup
├── session.js createSession, validateSession, deleteSession
├── email.js tokens de vérification + envoi des e-mails
├── password.js hashPassword, verifyPassword, generateToken
├── db.js createTables, dropTables
├── storage-policies.js politiques d'accès au stockage
├── AuthPage.server.js page RSC racine (route catch-all)
├── AuthPage.client.js shell client
├── GUIDE-custom-login.md guide pour les pages personnalisées
├── components/
│ └── AuthPageHeader.js
├── pages/
│ ├── index.js re-export
│ ├── LoginPage.client.js
│ ├── RegisterPage.client.js
│ ├── ForgotPasswordPage.client.js
│ ├── ResetPasswordPage.client.js
│ ├── ConfirmEmailPage.client.js
│ ├── SetupAccountPage.client.js
│ └── LogoutPage.client.js
└── templates/
├── VerificationEmail.js
├── PasswordResetEmail.js
├── PasswordChangedEmail.js
├── EmailChangeConfirmEmail.js
├── EmailChangeNotifyEmail.js
└── InvitationEmail.js
Import
import { getSession, loginAction, logoutAction } from '@zen/core/features/auth/actions';
import { LoginPage, RegisterPage } from '@zen/core/features/auth/pages';
import { validateSession, createSession } from '@zen/core/features/auth';
Pages intégrées
La route catch-all app/auth/[...auth]/page.js suffit pour exposer toutes les pages sans configuration supplémentaire.
// app/auth/[...auth]/page.js
export { default } from '@zen/core/features/auth/server';
| Route | Page |
|---|---|
/auth/login |
Connexion |
/auth/register |
Inscription |
/auth/forgot |
Mot de passe oublié |
/auth/reset |
Réinitialisation du mot de passe |
/auth/confirm |
Vérification de l'adresse courriel |
/auth/setup |
Configuration du compte après invitation admin |
/auth/logout |
Déconnexion |
Server actions
Toutes les actions sont dans @zen/core/features/auth/actions. Elles attendent un FormData sauf getSession, setSessionCookie et refreshSessionCookie.
getSession()
Lit le cookie de session et retourne la session courante, ou null si l'utilisateur n'est pas connecté. Renouvelle automatiquement le cookie si la session a été rafraîchie.
const session = await getSession();
if (!session?.user) redirect('/auth/login');
// session.user, session.session disponibles
loginAction(formData)
Authentifie l'utilisateur et pose un cookie HttpOnly. Applique le rate limiting par IP et les vérifications anti-bot.
const result = await loginAction(formData);
// { success: true, user } ou { success: false, error }
registerAction(formData)
Crée un compte et envoie l'e-mail de vérification.
| Champ | Description |
|---|---|
email |
Adresse courriel |
password |
Mot de passe |
name |
Nom d'affichage |
logoutAction()
Invalide la session en base et supprime le cookie.
forgotPasswordAction(formData)
Envoie un lien de réinitialisation si un compte existe pour l'adresse fournie. La réponse ne révèle pas si le compte existe.
resetPasswordAction(formData)
Vérifie le token puis met à jour le mot de passe.
| Champ | Description |
|---|---|
email |
Adresse courriel |
token |
Token reçu par e-mail |
newPassword |
Nouveau mot de passe |
verifyEmailAction(formData)
Vérifie le token de confirmation et marque l'adresse comme vérifiée.
| Champ | Description |
|---|---|
email |
Adresse courriel |
token |
Token reçu par e-mail |
setupAccountAction(formData)
Vérifie le token d'invitation, crée le compte credential et marque l'adresse comme vérifiée. Appelée depuis /auth/setup après qu'un admin a créé le compte sans mot de passe.
| Champ | Description |
|---|---|
email |
Adresse courriel |
token |
Token reçu dans le courriel d'invitation |
newPassword |
Mot de passe choisi |
confirmPassword |
Confirmation du mot de passe |
setSessionCookie(token)
Valide le token contre la base avant de l'écrire dans le cookie HttpOnly. À utiliser après une authentification externe ou OAuth.
refreshSessionCookie(token)
Revalide le token et prolonge la durée de vie du cookie (30 jours).
Routes API REST
Les routes sont enregistrées automatiquement sous le préfixe /zen/api. L'authentification est appliquée par le routeur avant chaque handler.
Utilisateurs
| Méthode | Route | Auth | Description |
|---|---|---|---|
GET |
/zen/api/users |
admin | Liste paginée des utilisateurs |
POST |
/zen/api/users |
admin | Créer un utilisateur (avec ou sans invitation) |
GET |
/zen/api/users/:id |
admin | Détail d'un utilisateur |
PUT |
/zen/api/users/:id |
admin | Modifier name, role, email_verified |
PUT |
/zen/api/users/:id/email |
admin | Changer l'adresse courriel |
PUT |
/zen/api/users/:id/password |
admin | Définir un mot de passe |
POST |
/zen/api/users/:id/send-password-reset |
admin | Envoyer un lien de réinitialisation |
GET |
/zen/api/users/:id/roles |
admin | Lister les rôles de l'utilisateur |
POST |
/zen/api/users/:id/roles |
admin | Assigner un rôle |
DELETE |
/zen/api/users/:id/roles/:roleId |
admin | Révoquer un rôle |
Profil (utilisateur connecté)
| Méthode | Route | Description |
|---|---|---|
PUT |
/zen/api/users/profile |
Modifier le nom |
POST |
/zen/api/users/profile/email |
Initier un changement d'adresse courriel |
GET |
/zen/api/users/email/confirm |
Confirmer le changement d'adresse |
POST |
/zen/api/users/profile/password |
Changer le mot de passe |
POST |
/zen/api/users/profile/picture |
Téléverser une photo de profil |
DELETE |
/zen/api/users/profile/picture |
Supprimer la photo de profil |
GET |
/zen/api/users/profile/sessions |
Lister les sessions actives |
DELETE |
/zen/api/users/profile/sessions |
Révoquer toutes les sessions |
DELETE |
/zen/api/users/profile/sessions/:sessionId |
Révoquer une session |
Rôles
| Méthode | Route | Description |
|---|---|---|
GET |
/zen/api/roles |
Lister les rôles |
POST |
/zen/api/roles |
Créer un rôle |
GET |
/zen/api/roles/:id |
Détail d'un rôle |
PUT |
/zen/api/roles/:id |
Modifier un rôle |
DELETE |
/zen/api/roles/:id |
Supprimer un rôle |
Invitation par l'admin
Un administrateur peut créer un utilisateur depuis /admin/users → Nouvel utilisateur. Deux flux selon si un mot de passe est fourni :
Avec mot de passe : l'utilisateur est créé avec email_verified = true et un compte credential. Il peut se connecter immédiatement.
Sans mot de passe : l'utilisateur est créé avec email_verified = false et aucun compte credential. Un token account_setup (48 h) est généré et un courriel d'invitation est envoyé. L'utilisateur clique sur le lien /auth/setup?email=X&token=Y, choisit son mot de passe, et le compte est activé (email_verified = true) en une seule étape — le passage par le lien vaut confirmation du courriel.
Admin crée l'utilisateur (sans mdp)
→ POST /zen/api/users
→ zen_auth_users créé (email_verified: false)
→ token account_setup enregistré dans zen_auth_verifications (48 h)
→ courriel InvitationEmail envoyé
Utilisateur clique sur le lien /auth/setup
→ SetupAccountPage (setupAccountAction)
→ token vérifié
→ zen_auth_accounts créé avec mot de passe haché
→ email_verified = true
→ token supprimé
→ redirection vers /auth/login
Sécurité
Rate limiting par IP. Les actions register, login, forgot_password, reset_password et verify_email sont limitées par adresse IP. Quand l'IP est inconnue (pas de proxy configuré), le rate limiting est suspendu et un avertissement opérateur est émis une seule fois. Activer avec ZEN_TRUST_PROXY=true derrière un reverse proxy vérifié.
Champs anti-bot. Chaque formulaire embarque un champ honeypot (_hp) et un timestamp de chargement (_t). Une soumission trop rapide (moins de 1,5 s), trop ancienne (plus de 10 min) ou avec un honeypot rempli est rejetée.
Cookie HttpOnly. Le token de session n'est jamais exposé à JavaScript. setSessionCookie et refreshSessionCookie valident le token en base avant d'écrire le cookie pour éviter qu'un token arbitraire soit accepté.
Erreurs opaques. Les erreurs internes sont loguées côté serveur et remplacées par un message générique côté client. Seules les UserFacingError (token expiré, etc.) remontent verbatim.
Base de données
db.js expose createTables() et dropTables(), appelés par initializeZen().
| Table | Contenu |
|---|---|
zen_auth_users |
Utilisateurs (id, email, name, role, email_verified, image) |
zen_auth_sessions |
Sessions actives avec IP et user-agent |
zen_auth_accounts |
Comptes liés à un provider (credential, OAuth) |
zen_auth_verifications |
Tokens de vérification d'e-mail et de réinitialisation |
Pages personnalisées
Pour envelopper les pages auth dans un layout existant, voir GUIDE-custom-login.md. Le guide couvre le pattern serveur/client, les props de chaque composant et la protection de route.