Gestion de session et cookies sécurisés
Implémente la gestion de session, cookies httpOnly et redirections.
Accéder gratuitement à cette formation
Renseigne ton email pour débloquer immédiatement cette formation gratuite.
Bienvenue dans cette leçon dédiée à la persistance de l’authentification grâce aux cookies sécurisés sous Next.js 15. Tu vas découvrir comment :
- Générer et lire un cookie httpOnly et secure.
- Extraire l’ID de session pour authentifier tes pages.
- Protéger les routes critiques (requireUser).
- Implémenter un logout fiable.
Pourquoi utiliser des cookies sécurisés ?
La session en cookie permet de :
- Stocker un identifiant sans l’exposer côté client (httpOnly).
- Limiter la portée du cookie (path, maxAge).
- Activer le flag secure pour HTTPS en production.
Ces bonnes pratiques réduisent les risques de vol de session et d’attaque XSS.
API cookies de Next.js
Next.js 15 expose un helper cookies() utilisable exclusivement dans un contexte serveur ('use server').
Tu peux :
- set pour stocker un cookie.
- get pour le récupérer.
- delete pour effacer la session.
Implémenter session.ts
1'use server'2import { cookies } from 'next/headers'3import { prisma } from './db'45const sessionCookieName = 'user_session'67export async function setUserId({ userId }: { userId: string }) {8const cookieStore = await cookies()9cookieStore.set(sessionCookieName, userId, {10secure: process.env.NODE_ENV === 'production', // @callout: actif en prod seulement11httpOnly: true, // @callout: interdit l’accès en JS12path: '/', // ^? accessible sur tout le domaine13maxAge: 60 * 60 * 24 * 30, // ^? durée de vie : 30 jours14})15}1617export async function getUserId() {18const cookieStore = await cookies()19return cookieStore.get(sessionCookieName)?.value ?? null20}2122export async function logout() {23const cookieStore = await cookies()24cookieStore.delete(sessionCookieName)25}2627export async function getOptionalUser() {28const userId = await getUserId()29if (!userId) return null30return prisma.user.findUnique({31where: { id: Number(userId) },32select: { email: true },33})34}3536export async function requireUser() {37const user = await getOptionalUser()38if (!user) throw new Error('Utilisateur non authentifié')39return user40}
Tip
httpOnly empêche tout script d’y accéder, et secure ne lèvera le cookie qu’en HTTPS.
Intégration dans l’authentification
Dans ton server/auth.ts, après avoir validé l’utilisateur, il suffit d’appeler setUserId pour déclencher la création du cookie, puis redirect('/') pour renvoyer la home page.
1'use server'2import { redirect } from 'next/navigation'3import { setUserId } from './session'4import { compare } from 'bcryptjs'5import { prisma } from './db'67export async function login(8prevState: unknown,9formData: FormData10) {11'use server'12const email = formData.get('email') as string13const password = formData.get('password') as string1415const user = await prisma.user.findUnique({ where: { email } })16if (!user) throw new Error('Utilisateur non trouvé')1718const valid = await compare(password, user.password)19if (!valid) throw new Error('Mot de passe invalide')2021await setUserId({ userId: user.id.toString() })22redirect('/')23}
Protéger une page avec requireUser
Pour sécuriser l’accès à /history, utilise requireUser() en début de ta Server Component :
1'use server'2import { requireUser } from '../server/session'3import { HistoryList } from './HistoryList'4import { getHistory } from '../server/history'56export default async function HistoryPage() {7const user = await requireUser()8const items = await getHistory()9return <HistoryList user={user} initialHistoryItems={items} />10}
Erreur de redirection
requireUser() lève une exception si l’utilisateur n’est pas connecté : pense à gérer le catch pour rediriger.
Ajouter le bouton de logout dans le layout
Dans app/layout.tsx, fais appel à getOptionalUser() pour afficher l’email et un formulaire Logout.
Le form action peut être une lambda 'use server' qui appelle logout().
1...2export default async function RootLayout({ children }: { children: ReactNode }) {3const user = await getOptionalUser()4return (5<html lang="fr">6<body>7<nav>8{user ? (9<>10<span>{user.email}</span>11<form action={async () => {12'use server'13await logout()14}}>15<button type="submit">Logout</button>16</form>17</>18) : (19<>20<Link href="/login">Login</Link>21<Link href="/register">Register</Link>22</>23)}24</nav>25{children}26</body>27</html>28)29}
Tip
En mode développement, ton cookie n’est pas chiffré. Sois sûr d’avoir secure: true en production.
Récapitulatif des points clés
- cookies() : set, get, delete.
httpOnly+securerenforcent la sécurité.- Mettre
path: '/'etmaxAgeapproprié. - Utiliser
getOptionalUser()pour récupérer le user ou null. requireUser()pour forcer l’accès authentifié.- Intégration avec Server Actions + redirect de Next.js 15.
Exercices rapides
-
Configurer un cookie
Dansapp/server/session.ts, modifiemaxAgepour 7 jours au lieu de 30 et teste la suppression automatique. -
Protéger une API route
Créeapp/api/profile/route.tsqui retourne les infos de l’utilisateur connecté ou 401 si non authentifié (utiliserequireUser()). -
Test de logout
Ajoute un message flash après déconnection :- Stocke un cookie temporaire
flash="À bientôt !" - Lis-le côté client dans le layout pour afficher la notification, puis supprime-le.
- Stocke un cookie temporaire
Bonne session !