Pourquoi Authentification dans NestJS ?
L'authentification est un élément clé des applications modernes, garantissant que seuls les utilisateurs autorisés peuvent accéder aux ressources protégées. Dans le contexte professionnel, une authentification efficace permet de maintenir la sécurité et l'intégrité des données. Un cas concret est un site web de gestion de projet, où seules les tâches assignées à un utilisateur devraient être visibles.
Prerequis
- Connaissance de base en JavaScript et TypeScript
- Compréhension de NestJS et son architecture (Modules, Controllers, Services)
- Familiarité avec le concept de guards (gardiens) dans NestJS
- Un environnement de développement Node.js installé (version recommandée : 14.0+)
Concepts fondamentaux
1. Guards (Gardiens)
Un guard est un mécanisme pour vérifier les conditions d'autorisation avant que le handler (la fonction qui gère la requête) ne soit exécuté. NestJS utilise les guards pour implémenter l'authentification.
// auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
// Vérifiez ici si l'utilisateur est authentifié
const request = context.switchToHttp().getRequest();
if (request.isAuthenticated()) {
return true;
}
return false;
}
}
2. Strategies d'authentification
Une stratégie d'authentification définit comment un utilisateur peut être authentifié. NestJS prend en charge plusieurs stratégies comme JWT, OAuth2, etc.
// jwt.strategy.ts
import { Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: (req) => req.headers.authorization.split(' ')[1],
ignoreExpiration: false,
secretOrKey: 'your-secret-key',
});
}
async validate(payload: any) {
return { userId: payload.sub, username: payload.username };
}
}
3. Middleware d'authentification
Un middleware est une fonction qui s'exécute avant le traitement de la requête. Il peut être utilisé pour ajouter des informations d'authentification dans le contexte de la requête.
// auth.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class AuthMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
// Vérifiez ici si l'utilisateur est authentifié
if (req.headers.authorization) {
next();
} else {
res.status(401).send('Unauthorized');
}
}
}
Mise en pratique : projet fil rouge
Étape 1 : Création du projet NestJS
nest new auth-project
cd auth-project
Étape 2 : Installation des dépendances
npm install --save @nestjs/passport passport passport-jwt jsonwebtoken
Étape 3 : Configuration de la stratégie JWT
Créez un fichier jwt.strategy.ts dans le dossier src/auth.
// src/auth/jwt.strategy.ts
import { Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: (req) => req.headers.authorization.split(' ')[1],
ignoreExpiration: false,
secretOrKey: 'your-secret-key',
});
}
async validate(payload: any) {
return { userId: payload.sub, username: payload.username };
}
}
Étape 4 : Création du module d'authentification
Créez un fichier auth.module.ts dans le dossier src/auth.
// src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
@Module({
providers: [AuthService, JwtStrategy],
})
export class AuthModule {}
Étape 5 : Configuration de la stratégie JWT dans le module principal
Modifiez app.module.ts pour inclure le module d'authentification.
// src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthModule } from './auth/auth.module';
@Module({
imports: [AuthModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Étape 6 : Création d'un service d'authentification
Créez un fichier auth.service.ts dans le dossier src/auth.
// src/auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import * as jwt from 'jsonwebtoken';
@Injectable()
export class AuthService {
async generateToken(userId: number, username: string): Promise<string> {
const payload = { sub: userId, username };
return jwt.sign(payload, 'your-secret-key', { expiresIn: '1h' });
}
}
Étape 7 : Création d'un controller pour les authentifiations
Créez un fichier auth.controller.ts dans le dossier src/auth.
// src/auth/auth.controller.ts
import { Controller, Post, Body, UseGuards } from '@nestjs/common';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('login')
async login(@Body() body): Promise<{ token: string }> {
const userId = 1; // ID de l'utilisateur fictif
const username = 'user'; // Nom d'utilisateur fictif
const token = await this.authService.generateToken(userId, username);
return { token };
}
}
Étape 8 : Ajout du guard d'authentification à un controller
Ajoutez le guard JwtStrategy au controlleur qui nécessite une authentification.
// src/tasks/tasks.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtStrategy } from '../auth/jwt.strategy';
@Controller('tasks')
@UseGuards(JwtStrategy)
export class TasksController {
@Get()
getTasks() {
return 'Liste des tâches';
}
}
Erreurs frequentes et debugging
Erreur 1 : JWT invalide
// ❌ Mauvais
const token = 'invalid-jwt-token';
// ✅ Correct
const token = 'your-valid-jwt-token';
Erreur 2 : Strategy non définie
// ❌ Mauvais
import { Strategy } from 'passport-jwt';
// ✅ Correct
import { PassportStrategy } from '@nestjs/passport';
Erreur 3 : Port non défini
// ❌ Mauvais
const PORT = process.env.PORT || '3000';
// ✅ Correct
const PORT = parseInt(process.env.PORT, 10) || 3000;
Pour aller plus loin
1. Sécurité des tokens JWT
En savoir plus sur les bonnes pratiques pour sécuriser les tokens JWT.
2. Authentification OAuth2
Explorer l'authentification OAuth2 pour une meilleure sécurité et flexibilité.
3. Throttling (Throttlement)
Ajouter un système de limitation des requêtes pour prévenir les attaques par déni de service.
Défi pratique
Implémenter un système d'enregistrement et de connexion utilisateurs avec des tokens JWT.