Gérer l'authentification (email et mot de passe) avec NextAuth et NextJs App router


Tout d’abord qu’est ce NextAuth ?
NextAuth est une librairie d'authentification pour Next.js qui permet d'intégrer rapidement différentes stratégies d'authentification, y compris l'authentification par email et mot de passe, avec Google, Apple, Github et plus encore. Il simplifie la gestion des sessions, des tokens JWT et des connexions sécurisées.
Ce guide vous montrera comment implémenter l'authentification avec email et mot de passe et sécuriser nos route avec NextAuth.js dans une application Next.js utilisant le routeur App (app/
).
Prérequis
Avant de commencer, assurez-vous d'avoir :
Une application Next.js avec app router contenant le dossier (
app/
)NextAuth.js installé :
npm install next-auth
Petit disclaimer
Ce guide couvrira uniquement l’authenfication via credential (email et mot de passe) avec NextAuth. Pour l’authentification avec d’autres provider comme Google, Github etc …, la documentation de NextAuth explique bien le sujet.
Nous verrons également comment implémenter une politique de protection des routes en utilisant un middleware.
Entrons dans le vif du sujet en suivant ces quelques étapes, pour faire notre configuration :
1. Création du gestionnaire d'authentification
NextAuth recommande de définir la configuration d'authentification dans un fichier auth.ts
.
Créez donc un fichier lib/auth.ts
à la racine /
du projet src/
si vous l’utilisez et configurez NextAuth
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { compare } from "bcryptjs";
import { getUserByEmail } from "@/lib/userService";
export const authOptions = {
providers: [
CredentialsProvider({
name: "Email & Mot de passe",
credentials: {
email: { label: "Email", type: "email", placeholder: "user@example.com" },
password: { label: "Mot de passe", type: "password" },
},
async authorize(credentials) {
// faire ici la vérification d'authenticité de l'utilisateur,
// ça peut être une requête vers votre API ou tout autre service backend
// ensuite nous retournons l'utilisateur user que nous allons stocker en session
...user
return { id: user.id, name: user.name, email: user.email, token: user.token };
},
}),
],
callbacks: {
async session({ session, token }) {
session.user.id = token.id;
session.user.token= token.role;
return session;
},
},
pages: {
signIn: "/auth/signin",
},
session: {
strategy: 'jwt',
maxAge: 30 * 24 * 60 * 60, // 30 jours
},
secret: process.env.NEXTAUTH_SECRET,
};
export const { handlers, auth } = NextAuth(authOptions);
Chose importante à noter : nous exportons une constante authOptions dans la laquelle il y a pas mal de configurations.
De façons détaillée:
-providers
[] : c’est un tableau qui contiendra toute les méthodes de connexion que nous voudrons configurer. Ici nous configurons uniquement la connexion par email et mot de passe avec CredendialsProviders
-callbacks
: c’est là que nous allons définir tout ce qui se passe après une connexion avec succès. Dans le cas d’espèse nous stockons en session le informations de l’utilsateur
- pages
: ici next-auth nous devons définir les page de fallback ou redirection pour des actions comme le signIn
pour la connexion, error
pour les erreurs liés à une erreur de connexion, signout
où rediriger l’utilisateur quand il se déconnecte, la liste complète se trouce ici
-session
: ici on défini la comment sera géré la session des utilisateurs
-secret
: ceci est une configuration importante à faire. C’est une clé secrette que nous devons générer et stocker dans nos variables d’environnement dans env.local
que NextAuth utilisera en production pour sécuriser les requêtes qu’elle effectuera à partir du serveur de notre application. Vous pouvez en lire plus ici.
Ou la générer avec cette commande
$ openssl rand -base64 32
2. Configuration d’une route API d'authentification
Dans app/api/auth/[...nextauth]/route.ts
, nous allons exporter des méthodes GET et POST à partir handler configuré dans lib/auth.ts
.
⚠️ Faite bien attention à respecter la casse en écrivant [...nextauth]
, NextJs est sensible à la case .
import NextAuth from 'next-auth';
import { authOptions } from '@/lib/auth';
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
3. Ajout d'un formulaire de connexion
Dans cette 3e étape nous allons nous utiliser la méthode signIn de NextAuth pour connecter notre utilisateur, nous allons également gérer quelques erreurs.
Nous allons créer une page app/auth/signin.tsx
"use client";
import { signIn } from "next-auth/react";
import { useState } from "react";
import { redirect } from "next/navigation";
export default function SignIn() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const result = await signIn("credentials", {
email,
password,
redirect: false,
});
if (result?.error) {
setError(result.error);
} else {
redirect("/dashboard");
}
};
return (
<div>
<h2>Connexion</h2>
{error && <p style={{ color: "red" }}>{error}</p>}
<form onSubmit={handleSubmit}>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<input
type="password"
placeholder="Mot de passe"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<button type="submit">Se connecter</button>
</form>
</div>
);
}
Dans ce block nous avons un simple formulaire de connexion pour authenfier l’utilisateur. Ici nous utiiisons la méthode signIn
de NextAuth à laquelle nous passons l’email et le mot de passe de l’utilisateur pour vérifier si il existe bien dans notre base de donnée avant de le laisser passer. Petit rappel c’est la méthode authorize
de lib/auth.ts
qui nous fait cette vérification.
4. Protection des routes ou pages
Pour protéger nos route nous allons ajouter un middleware dans notre dossier app app/middleware.ts
⚠️ Attention à bien avoir le middleware dans le dossier app/
sinon celà ne fonctionnera pas !
Ici nous allons supposer que nous voulons protéger toute les routes de notre dossier dashboard/
import { auth } from "@/lib/auth";
import { NextResponse } from "next/server";
export async function middleware(req) {
const session = await auth();
// ici on tente de récupérer la session de l'utilisateur
if (!session && req.nextUrl.pathname.startsWith("/dashboard")) {
return NextResponse.redirect(new URL("/auth/signin", req.url));
}
// si la session n'existe pas alors que l'utilisateur tente d'accéder
// au dashboard on le redirige vers la page de connexion
}
export const config = {
matcher: ["/dashboard/:path*"],
};
// ici la protection dans la méthode middleware s'applique à la route /dashboard
// mais aussi tout ce qui peut suivre ex /dashboard/
Et voilà 🎊🎊
Pour aller un peu plus loin nous pouvons afficher les informations de session de l’utilisateur lorsqu’il se connecte.
Pour cela nous allons:
envelopper notre application avec SessionProvider pour accéder aux informations de sessions partout dans l’application
importer le hook useSession pour récupérer les info de sesssion et les afficher
Envelopper l’application avec SessionProvider
Dans notre RootLayout, on pourrait penser faire ceci
// dans app/layout.tsx
import { getSession } from "@/lib/auth"
import { SessionProvider } from "next-auth/react"
// Ne fonctionnera pas
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const session = await getSession()
return (
<html lang="en">
<body>
<SessionProvider session={session}>
{children}
</SessionProvider>
</body>
</html>
)
}
Mais cela ne fonctionnera pas. Le problème vient du fait que getSession()
est une fonction asynchrone qui ne peut pas être exécutée dans un composant React Server Component (RSC) comme RootLayout
Nous allons donc créer un provider personnalisé que nous allons importer dans le RootLayout.
Créons donc un providers/Authprovider.tsx
// dans app/providers.tsx
"use client"
import type { Session } from "next-auth"
import { SessionProvider } from "next-auth/react"
// // Ceci fonctionnera
export default function AurhProvider({ session, children }: { session: Session | null, children: React.ReactNode }) {
return (
<SessionProvider session={session}>
{children}
</SessionProvider>
)
}
Ainsi dans notre RootLayout
// dans app/layout.tsx
import { getSession } from "@/lib/auth"
import Providers from "./providers/AuthProvider"
// Ceci fonctionnera
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const session = await getSession()
return (
<html lang="en">
<body>
<AuthProviders session={session}>
{children}
</AuthProviders>
</body>
</html>
)
}
Avec ceci nous pourrons dans un n’importe quel composant client faire
// components/client-component.tsx
"use client"
import { useSession } from "next-auth/react"
export default function MyClientComponent() {
const { data: session, status } = useSession()
const { user } = session;
// ... et là nous pouvons afficher les information de sessions ou les utiliser
return (
// du code jsx...
)
}
Ou dans un composant serveur
// components/server-component.tsx
import { getSession } from "@/auth"
export default function MyServerComponent() {
const session = await getSession()
const { user } = session;
// ... et là nous pouvons utiliser les utiliser par exemple le token de l'utilisateur ou son id
// pour effectuer des requêtes côté serveur
return (
// du code jsx...
)
}
Fécilicitation ci vous être venu jusqu’à cette étape, vous avez donc une authentification sécurisé dans votre application gérée avec Next Auth;
Happy coding 🎊🎊
Subscribe to my newsletter
Read articles from arnaud akotonou directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

arnaud akotonou
arnaud akotonou
Passionate software developer and tech blogger with a focus on frontend development, deployment strategies, and Backend-as-a-Service (BaaS) solutions. I share insights, tutorials, and practical tips to help developers navigate modern tech stacks, streamline deployment processes, and leverage backend solutions for scalable applications. My goal is to empower the dev community with actionable knowledge, making complex concepts more accessible and implementation-ready.