Ayez plus de contrôle sur la logique d'authentification de votre application Next.js grâce à la mise en œuvre d'une authentification personnalisée basée sur JWT.
L'authentification par jeton est une stratégie populaire utilisée pour protéger les applications Web et mobiles contre tout accès non autorisé. Dans Next.js, vous pouvez utiliser les fonctionnalités d'authentification fournies par Next-auth.
Vous pouvez également choisir de développer un système d'authentification personnalisé basé sur des jetons à l'aide de jetons Web JSON (JWT). Ce faisant, vous vous assurez d'avoir plus de contrôle sur la logique d'authentification; essentiellement, personnaliser le système pour qu'il corresponde précisément aux exigences de votre projet.
Configurer un projet Next.js
Pour commencer, installez Next.js en exécutant la commande ci-dessous sur votre terminal.
npx create-next-app@latest next-auth-jwt --experimental-app
Ce guide utilisera Next.js 13 qui inclut le répertoire de l'application.
Ensuite, installez ces dépendances dans votre projet en utilisant npm, le gestionnaire de packages de nœuds.
npm install jose universal-cookie
José est un module JavaScript qui fournit un ensemble d'utilitaires permettant de travailler avec les jetons Web JSON pendant que le cookie-universel La dépendance fournit un moyen simple de travailler avec les cookies du navigateur dans les environnements côté client et côté serveur.
Vous pouvez trouver le code de ce projet dans ce Dépôt GitHub.
Créer l'interface utilisateur du formulaire de connexion
Ouvrez le src/application répertoire, créez un nouveau dossier et nommez-le se connecter. Dans ce dossier, ajoutez un nouveau page.js fichier et incluez le code ci-dessous.
"use client";
import { useRouter } from"next/navigation";
exportdefaultfunctionLoginPage() {
return (
Le code ci-dessus crée un composant fonctionnel de page de connexion qui affichera un simple formulaire de connexion sur le navigateur pour permettre aux utilisateurs de saisir un nom d'utilisateur et un mot de passe.
Le utiliser le client L'instruction dans le code garantit qu'une limite est déclarée entre le code serveur uniquement et le code client uniquement dans le application annuaire.
Dans ce cas, il est utilisé pour déclarer que le code présent sur la page de connexion, notamment le handleEnvoyerla fonction n'est exécutée que sur le client; sinon, Next.js générera une erreur.
Maintenant, définissons le code du handleEnvoyer fonction. À l’intérieur du composant fonctionnel, ajoutez le code suivant.
const router = useRouter();
const handleSubmit = async (event) => {
event.preventDefault();
const formData = new FormData(event.target);
const username = formData.get("username");
const password = formData.get("password");
const res = await fetch("/api/login", {
method: "POST",
body: JSON.stringify({ username, password }),
});
const { success } = await res.json();
if (success) {
router.push("/protected");
router.refresh();
} else {
alert("Login failed");
}
};
Pour gérer la logique d'authentification de connexion, cette fonction capture les informations d'identification de l'utilisateur à partir du formulaire de connexion. Il envoie ensuite une requête POST à un point de terminaison d'API transmettant les détails de l'utilisateur pour vérification.
Si les informations d'identification sont valides, indiquant que le processus de connexion a réussi, l'API renvoie un statut de réussite dans la réponse. La fonction de gestionnaire utilisera ensuite le routeur de Next.js pour diriger l'utilisateur vers une URL spécifiée, dans ce cas, le protégé itinéraire.
Définir le point de terminaison de l'API de connexion
À l'intérieur de src/application répertoire, créez un nouveau dossier et nommez-le API. Dans ce dossier, ajoutez un nouveau connexion/route.js fichier et incluez le code ci-dessous.
import { SignJWT } from"jose";
import { NextResponse } from"next/server";
import { getJwtSecretKey } from"@/libs/auth";
exportasyncfunctionPOST(request) {
const body = await request.json();
if (body.username "admin" && body.password "admin") {
const token = awaitnew SignJWT({
username: body.username,
})
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime("30s")
.sign(getJwtSecretKey());
const response = NextResponse.json(
{ success: true },
{ status: 200, headers: { "content-type": "application/json" } }
);
response.cookies.set({
name: "token",
value: token,
path: "/",
});
return response;
}
return NextResponse.json({ success: false });
}
La tâche principale de cette API est de vérifier les informations de connexion transmises dans les requêtes POST à l'aide de données fictives.
Une fois la vérification réussie, il génère un jeton JWT crypté associé aux détails de l'utilisateur authentifié. Enfin, il envoie une réponse réussie au client, incluant le jeton dans les cookies de réponse; sinon, il renvoie une réponse d'état d'échec.
Implémenter la logique de vérification des jetons
La première étape de l'authentification par jeton consiste à générer le jeton après un processus de connexion réussi. L'étape suivante consiste à implémenter la logique de vérification des jetons.
Essentiellement, vous utiliserez le jwtVérifier fonction assurée par le José module pour vérifier les jetons JWT transmis avec les requêtes HTTP ultérieures.
Dans le src répertoire, créez un nouveau libs/auth.js fichier et incluez le code ci-dessous.
import { jwtVerify } from"jose";
exportfunctiongetJwtSecretKey() {
const secret = process.env.NEXT_PUBLIC_JWT_SECRET_KEY;
if (!secret) {
thrownewError("JWT Secret key is not matched");
}
returnnew TextEncoder().encode(secret);
}
exportasyncfunctionverifyJwtToken(token) {
try {
const { payload } = await jwtVerify(token, getJwtSecretKey());
return payload;
} catch (error) {
returnnull;
}
}
La clé secrète est utilisée pour signer et vérifier les jetons. En comparant la signature du jeton décodée à la signature attendue, le serveur peut vérifier efficacement que le jeton fourni est valide et, finalement, autoriser les demandes des utilisateurs.
Créer .env fichier dans le répertoire racine et ajoutez une clé secrète unique comme suit :
NEXT_PUBLIC_JWT_SECRET_KEY=your_secret_key
Créer un itinéraire protégé
Maintenant, vous devez créer un itinéraire auquel seuls les utilisateurs authentifiés peuvent accéder. Pour ce faire, créez un nouveau protégé/page.js fichier dans le src/application annuaire. Dans ce fichier, ajoutez le code suivant.
exportdefaultfunctionProtectedPage() {
return<h1>Very protected pageh1>;
}
Créer un hook pour gérer l'état d'authentification
Créez un nouveau dossier dans le src répertoire et nommez-le crochets. Dans ce dossier, ajoutez un nouveau utiliserAuth/index.js fichier et incluez le code ci-dessous.
"use client" ;
import React from"react";
import Cookies from"universal-cookie";
import { verifyJwtToken } from"@/libs/auth";exportfunctionuseAuth() {
const [auth, setAuth] = React.useState(null);
const getVerifiedtoken = async () => {
const cookies = new Cookies();
const token = cookies.get("token")?? null;
const verifiedToken = await verifyJwtToken(token);
setAuth(verifiedToken);
};
React.useEffect(() => {
getVerifiedtoken();
}, []);
return auth;
}
Ce hook gère l'état d'authentification côté client. Il récupère et vérifie la validité du jeton JWT présent dans les cookies à l'aide du vérifierJwtToken fonction, puis définit les détails de l'utilisateur authentifié sur le authentification État.
Ce faisant, il permet à d'autres composants d'accéder et d'utiliser les informations de l'utilisateur authentifié. Ceci est essentiel pour des scénarios tels que la mise à jour de l'interface utilisateur en fonction du statut d'authentification, l'envoi de requêtes API ultérieures ou le rendu de contenu différent en fonction des rôles d'utilisateur.
Dans ce cas, vous utiliserez le hook pour afficher différents contenus sur le maison itinéraire basé sur l’état d’authentification d’un utilisateur.
Une approche alternative que vous pourriez envisager consiste à gérer gestion de l'état à l'aide de Redux Toolkit ou en employant un outil de gestion d'état comme Jotai. Cette approche garantit que les composants peuvent obtenir un accès global à l'état d'authentification ou à tout autre état défini.
Allez-y et ouvrez le app/page.js, supprimez le code standard Next.js et ajoutez le code suivant.
"use client" ;
import { useAuth } from"@/hooks/useAuth";
import Link from"next/link";
exportdefaultfunctionHome() {
const auth = useAuth();
return<>Public Home Page</h1>
Le code ci-dessus utilise le utiliserAuth hook pour gérer l’état d’authentification. Ce faisant, il rend conditionnellement une page d'accueil publique avec un lien vers le se connecter itinéraire de page lorsque l'utilisateur n'est pas authentifié et affiche un paragraphe pour un utilisateur authentifié.
Ajoutez un middleware pour appliquer l'accès autorisé aux routes protégées
Dans le src répertoire, créez un nouveau middleware.js fichier et ajoutez le code ci-dessous.
import { NextResponse } from"next/server";
import { verifyJwtToken } from"@/libs/auth";const AUTH_PAGES = ["/login"];
const isAuthPages = (url) => AUTH_PAGES.some((page) => page.startsWith(url));
exportasyncfunctionmiddleware(request) {
const { url, nextUrl, cookies } = request;
const { value: token } = cookies.get("token")?? { value: null };
const hasVerifiedToken = token && (await verifyJwtToken(token));
const isAuthPageRequested = isAuthPages(nextUrl.pathname);if (isAuthPageRequested) {
if (!hasVerifiedToken) {
const response = NextResponse.next();
response.cookies.delete("token");
return response;
}
const response = NextResponse.redirect(new URL(`/`, url));
return response;
}if (!hasVerifiedToken) {
const searchParams = new URLSearchParams(nextUrl.searchParams);
searchParams.set("next", nextUrl.pathname);
const response = NextResponse.redirect(
new URL(`/login?${searchParams}`, url)
);
response.cookies.delete("token");
return response;
}return NextResponse.next();
}
exportconst config = { matcher: ["/login", "/protected/:path*"] };
Ce code middleware agit comme une garde. Il vérifie que lorsque les utilisateurs souhaitent accéder à des pages protégées, ils sont authentifiés et autorisés à accéder aux itinéraires, en plus de rediriger les utilisateurs non autorisés vers la page de connexion.
Sécuriser les applications Next.js
L'authentification par jeton est un mécanisme de sécurité efficace. Cependant, ce n'est pas la seule stratégie disponible pour protéger vos applications contre tout accès non autorisé.
Pour renforcer les applications face au paysage dynamique de la cybersécurité, il est important d'adopter une stratégie de sécurité complète. approche qui aborde de manière globale les failles et vulnérabilités potentielles en matière de sécurité afin de garantir une protection.