Introduction : pourquoi OAuth 2.0 et JWT sont la norme de l'autorisation moderne
\nDans le développement web moderne, l'authentification et l'autorisation sécurisées sont des composants essentiels de toute application. OAuth 2.0 et JWT (JSON Web Tokens) sont devenus la norme de facto pour la construction de systèmes distribués. OAuth 2.0 fournit un protocole d'accès délégué, tandis que JWT offre un format de token compact et autonome.
\nDans cet article, nous allons analyser, à l'aide d'exemples pratiques en TypeScript, comment implémenter le cycle complet d'autorisation : de l'émission des tokens à leur validation et leur renouvellement. Vous apprendrez à protéger les endpoints d'API, à implémenter les refresh tokens et à gérer les erreurs typiques.
\n\n1. Configuration de base du projet et installation des dépendances
\nPour commencer, créons un projet Node.js avec TypeScript. Assurez-vous d'avoir installé Node.js (version 18+) et npm.
mkdir oauth-jwt-typescript\ncd oauth-jwt-typescript\nnpm init -y\nnpm install express cors dotenv jsonwebtoken bcrypt\nnpm install -D typescript @types/express @types/cors @types/jsonwebtoken @types/bcrypt ts-node nodemon\nnpx tsc --init\nConfigurons tsconfig.json pour fonctionner avec les modules ES et les types stricts. Ajoutons les clés secrètes dans le fichier .env :
ACCESS_TOKEN_SECRET=your-super-secret-access-key\nREFRESH_TOKEN_SECRET=your-super-secret-refresh-key\nACCESS_TOKEN_EXPIRES_IN=15m\nREFRESH_TOKEN_EXPIRES_IN=7d\n\n2. Implémentation de la génération et de la vérification des tokens JWT
\nCréons un utilitaire src/utils/jwt.ts pour la gestion des tokens. Nous utiliserons deux types de tokens : Access Token (à courte durée de vie) et Refresh Token (à longue durée de vie).
import jwt from 'jsonwebtoken';\nimport dotenv from 'dotenv';\n\ndotenv.config();\n\ninterface TokenPayload {\n userId: string;\n email: string;\n role: 'user' | 'admin';\n}\n\nexport const generateAccessToken = (payload: TokenPayload): string => {\n return jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET!, {\n expiresIn: process.env.ACCESS_TOKEN_EXPIRES_IN || '15m',\n });\n};\n\nexport const generateRefreshToken = (payload: TokenPayload): string => {\n return jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET!, {\n expiresIn: process.env.REFRESH_TOKEN_EXPIRES_IN || '7d',\n });\n};\n\nexport const verifyAccessToken = (token: string): TokenPayload => {\n return jwt.verify(token, process.env.ACCESS_TOKEN_SECRET!) as TokenPayload;\n};\n\nexport const verifyRefreshToken = (token: string): TokenPayload => {\n return jwt.verify(token, process.env.REFRESH_TOKEN_SECRET!) as TokenPayload;\n};\nRemarque : nous utilisons un typage strict pour le payload. Cela garantit que le token contiendra toujours les champs nécessaires.
\n\n3. Création d'un middleware pour la protection des routes
\nImplémentons maintenant un middleware qui vérifiera la présence et la validité de l'Access Token dans l'en-tête Authorization. Créons src/middleware/auth.ts :
import { Request, Response, NextFunction } from 'express';\nimport { verifyAccessToken } from '../utils/jwt';\n\nexport interface AuthRequest extends Request {\n user?: {\n userId: string;\n email: string;\n role: 'user' | 'admin';\n };\n}\n\nexport const authenticate = (req: AuthRequest, res: Response, next: NextFunction): void => {\n const authHeader = req.headers['authorization'];\n const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN\n\n if (!token) {\n res.status(401).json({ error: 'Access token is required' });\n return;\n }\n\n try {\n const decoded = verifyAccessToken(token);\n req.user = decoded;\n next();\n } catch (error) {\n res.status(403).json({ error: 'Invalid or expired access token' });\n }\n};\n\nexport const authorizeRole = (...roles: string[]) => {\n return (req: AuthRequest, res: Response, next: NextFunction): void => {\n if (!req.user || !roles