L'authentification est l'un des aspects les plus critiques, mais souvent mal compris, du développement backend. En raison de sa complexité, les développeurs se tournent fréquemment vers des solutions tierces comme Auth0 ou Supabase. Bien qu'il s'agisse d'excellents outils, la création de votre propre système d'authentification peut offrir une plus grande flexibilité et un plus grand contrôle.
Dans ce guide, vous apprendrez comment implémenter un middleware d'authentification simple pour votre service API Express.js avec un minimum de dépendances. À la fin, vous aurez :
Ce guide se concentre sur la simplicité et évite d'utiliser des packages comme passeport.js pour réduire la complexité.
Tout d'abord, créez une table PostgreSQL pour stocker les comptes d'utilisateurs :
CREATE TABLE users ( "id" SERIAL PRIMARY KEY, "username" VARCHAR(255) UNIQUE NOT NULL, "password" VARCHAR(255) NOT NULL, "email" VARCHAR(255) UNIQUE, "created_at" TIMESTAMP NOT NULL DEFAULT NOW() );
Ensuite, créez un middleware d'authentification JWT pour protéger les points de terminaison de l'API. Cet exemple utilise le chiffrement symétrique. Pour les architectures de microservices, pensez à utiliser le chiffrement asymétrique avec une paire de clés publique/privée.
import jwt from "jsonwebtoken"; const JWT_SECRET_KEY = process.env.JWT_SECRET_KEY as string; // Randomly generated. Min length: 64 characters export const protectedRoute: RequestHandler = async (req, _, next) => { const authHeader = req.header("authorization"); if (!authHeader) { return next(notAuthenticated()); } const accessToken = authHeader.replace(new RegExp("\b[Bb]earer\s"), ""); try { const { userId } = validateJWT(accessToken); const user = await userRepository.getUserById(parseInt(userId)); if (user) { req.user = user; next(); } else { next(invalidAccessToken()); } } catch (err) { next(invalidAccessToken()); } }; const validateJWT = (token: string, verifyOptions?: jwt.VerifyOptions) => { const jwtVerifyOptions = Object.assign( { algorithms: "HS256" }, verifyOptions, { issuer: "yourAPI.com", audience: "yourAPI.com:client", } ); return jwt.verify(token, JWT_SECRET_KEY, jwtVerifyOptions) as T; };
Utilisez le middleware pour sécuriser les itinéraires :
import { protectedRoute } from "@/middleware/jwt"; router.get("/user", protectedRoute, async (req, res, next) => { const user = req.user!; res.json({ user }); });
Maintenant, implémentez des contrôleurs pour l'inscription et la connexion :
import argon from "argon2"; const signup = async (props) => { const { username, password, email } = props; await userRepo.getUser(username).then((res) => { if (res !== null) throw usernameNotAvailable(); }); const hashedPass = await argon.hash(password, { timeCost: 2, parallelism: 1, memoryCost: 19456, }); const newUser = await createUser({ username, hashedPass, email, }); const refreshToken = await generateRefreshToken(newUser.userId); const accessToken = generateAccessToken(newUser.userId); const { password: _, ...userRes } = newUser; return { user: userRes, accessToken, refreshToken }; };
const login = async (props) => { const { username, password } = props; const user = await getUser(username).then((res) => { if (res === null) throw invalidLoginCredentials(); return res; }); const isOk = await argon.verify(user.password, password); if (isOk) { const refreshToken = await generateRefreshToken(user.userId); const accessToken = generateAccessToken(user.userId); const { password: _, ...userRes } = user; return { user: userRes, accessToken, refreshToken }; } throw invalidLoginCredentials(); };
Les jetons d'actualisation fournissent une authentification à long terme. Créons une table de base de données pour les stocker :
CREATE TABLE refresh_tokens ( "id" SERIAL PRIMARY KEY, "token" UUID NOT NULL DEFAULT gen_random_uuid(), "token_family" UUID NOT NULL DEFAULT gen_random_uuid(), "user_id" INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, "active" BOOLEAN DEFAULT true, "expires_at" TIMESTAMP NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT NOW() );
import jwt from "jsonwebtoken"; const JWT_SECRET_KEY = process.env.JWT_SECRET_KEY as string; // Randomly generated. Min length: 64 characters const generateAccessToken = (userId: number) => { const jwtSignOptions = Object.assign( { algorithm: "HS256" }, {}, { issuer: "yourAPI.com", audience: "yourAPI.com:client", } ); return jwt.sign({ userId: userId.toString() }, JWT_SECRET_KEY, jwtSignOptions); }; const generateRefreshToken = async (userId: number, tokenFamily?: string) => { const expAt = new Date(new Date().getTime() + 31 * 24 * 60 * 60 * 1000); // Expire in 31 days const refreshTokenExp = expAt.toISOString(); const token = await createTokenQuery({ userId, tokenFamily, expiresAt: refreshTokenExp, }); return token; };
Implémentez une logique pour gérer les jetons d'actualisation en toute sécurité :
const refreshToken = async ({ token }: RefreshTokenSchema) => { const tokenData = await getRefreshToken(token); if (!tokenData) throw forbiddenError(); const { userId, tokenFamily, active } = tokenData; if (active) { // Token is valid and hasn't been used yet const newRefreshToken = await generateRefreshToken(userId, tokenFamily); const accessToken = generateAccessToken(userId); return { accessToken, refreshToken: newRefreshToken }; } else { // Previously refreshed token used, invalidate all tokens in family await invalidateRefreshTokenFamily(tokenFamily); throw forbiddenError(); } };
En savoir plus sur les jetons d'actualisation et la détection automatique de la réutilisation dans cet article Auth0.
En suivant ce guide, vous avez créé un système d'authentification simple et sécurisé pour votre API Node.js avec un minimum de dépendances. Cette approche garantit que vous avez un contrôle total et que vous adhérez aux meilleures pratiques modernes en matière de sécurité.
Si vous souhaitez économiser du temps et des efforts, consultez Vratix. Notre CLI open source peut mettre en place un projet Node.js entièrement fonctionnel avec authentification en quelques secondes. Explorez notre module d'authentification entièrement implémenté sur GitHub.
Ce guide vous a-t-il aidé ? Faites-le nous savoir dans les commentaires, ou connectez-vous avec nous sur X !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!