Authentification Passport
Passport est la bibliothèque d’authentification la plus populaire pour Node.js, connue et utilisée avec succès dans de nombreuses applications de production. Il est facile d’intégrer cette bibliothèque avec une application Nest en utilisant le module @nestjs/passport
. En gros, Passport exécute une série d’étapes pour :
- Authentifier un utilisateur en vérifiant ses “identifiants” (comme le nom d’utilisateur/mot de passe, le JSON Web Token (JWT), ou un jeton d’identité d’un fournisseur d’identité).
- Gérer l’état authentifié (en émettant un jeton portable, comme un JWT, ou en créant une session Express).
- Attacher des informations sur l’utilisateur authentifié à l’objet
Request
pour une utilisation ultérieure dans les gestionnaires de routes.
Dans ce chapitre, nous allons mettre en œuvre une solution complète d’authentification de bout en bout pour un serveur API RESTful en utilisant ces modules puissants et flexibles. Vous pouvez utiliser les concepts décrits ici pour mettre en œuvre n’importe quelle stratégie Passport pour personnaliser votre schéma d’authentification.
Exigences d’authentification
Flesherons nos exigences. Pour ce cas d’utilisation, les clients commenceront par s’authentifier avec un nom d’utilisateur et un mot de passe. Une fois authentifié, le serveur émettra un JWT qui peut être envoyé comme un jeton porteur dans un en-tête d’autorisation lors des demandes subséquentes pour prouver l’authentification. Nous créerons également une route protégée qui n’est accessible qu’aux demandes contenant un JWT valide.
Nous commencerons par la première exigence : authentifier un utilisateur. Nous étendrons ensuite cela en émettant un JWT. Enfin, nous créerons une route protégée qui vérifie la présence d’un JWT valide dans la demande.
D’abord, nous devons installer les paquets requis. Passport propose une stratégie appelée passport-local qui implémente un mécanisme d’authentification par nom d’utilisateur/mot de passe, ce qui convient à nos besoins pour cette partie de notre cas d’utilisation.
$ npm install --save @nestjs/passport passport passport-local$ npm install --save-dev @types/passport-local
Mise en œuvre des stratégies Passport
Nous sommes maintenant prêts à mettre en œuvre la fonctionnalité d’authentification. Nous commencerons par un aperçu du processus utilisé pour toute stratégie Passport. Il est utile de penser à Passport comme à un mini cadre en soi. L’élégance du cadre est qu’il abstrait le processus d’authentification en quelques étapes de base que vous personnalisez en fonction de la stratégie que vous implémentez. C’est comme un cadre parce que vous le configurez en fournissant des paramètres de personnalisation (sous forme d’objets JSON simples) et du code personnalisé sous forme de fonctions de rappel, que Passport appelle au moment approprié. Le module @nestjs/passport
enveloppe ce cadre dans un package de style Nest, facilitant son intégration dans une application Nest.
Nous allons utiliser @nestjs/passport
, mais d’abord, considérons comment vanilla Passport fonctionne.
async validate(username: string, password: string) { const user = await this.authService.validateUser(username, password); if (!user) { throw new UnauthorizedException(); } return user;}
Pour plus de détails sur le fonctionnement de l’authentification Passport, consultez la documentation officielle de Passport.
Route de connexion /auth/login
Avec la stratégie en place, nous pouvons maintenant mettre en œuvre une route /auth/login
minimaliste et appliquer le Guard intégré pour initier le flux passport-local.
Ouvrez le fichier app.controller.ts
et remplacez son contenu par ce qui suit :
@Controller()export class AppController { constructor(private authService: AuthService) {}
@UseGuards(LocalAuthGuard) @Post('auth/login') async login(@Request() req) { return this.authService.login(req.user); }}
Fonctionnalité JWT
Nous sommes prêts à passer à la partie JWT de notre système d’authentification. Analysons et affinons nos exigences :
- Autoriser les utilisateurs à s’authentifier avec un nom d’utilisateur/mot de passe, renvoyant un JWT pour une utilisation dans des appels suivants vers des points de terminaison API protégés.
- Créer des routes API qui sont protégées en fonction de la présence d’un JWT valide en tant que jeton porteur.
Nous allons devoir installer quelques autres paquets pour soutenir nos exigences JWT :
$ npm install --save @nestjs/jwt passport-jwt$ npm install --save-dev @types/passport-jwt
Le paquet @nestjs/jwt
est un paquet utilitaire qui aide avec la manipulation de JWT. Le paquet passport-jwt
est le paquet Passport qui implémente la stratégie JWT et @types/passport-jwt
fournit les définitions de type TypeScript.
Ouvrez le fichier auth.service.ts
et ajoutez la méthode login()
, puis importez le JwtService
tel que montré :
import { Injectable } from '@nestjs/common';import { UsersService } from '../users/users.service';import { JwtService } from '@nestjs/jwt';
@Injectable()export class AuthService { constructor(private usersService: UsersService, private jwtService: JwtService) {}
async validateUser(username: string, pass: string): Promise<any> { const user = await this.usersService.findOne(username); if (user && user.password === pass) { const { password, ...result } = user; return result; } return null; }
async login(user: any) { const payload = { username: user.username, sub: user.userId }; return { access_token: this.jwtService.sign(payload), }; }}
Implémentation de Passport JWT
Nous pouvons maintenant répondre à notre exigence finale : protéger les points de terminaison en exigeant qu’un JWT valide soit présent dans la demande. Passport peut également nous aider ici. Il fournit la stratégie passport-jwt pour sécuriser les points de terminaison RESTful avec des tokens JWT.
Créez un fichier appelé jwt.strategy.ts
dans le dossier auth
, et ajoutez le code suivant :
import { ExtractJwt, Strategy } from 'passport-jwt';import { PassportStrategy } from '@nestjs/passport';import { Injectable } from '@nestjs/common';import { jwtConstants } from './constants';
@Injectable()export class JwtStrategy extends PassportStrategy(Strategy) { constructor() { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: jwtConstants.secret, }); }
async validate(payload: any) { return { userId: payload.sub, username: payload.username }; }}
Assurez-vous que l’application fonctionne, et testez les routes utilisant cURL
.
$ curl http://localhost:3000/profile
Notez qu’au module AuthModule, nous avons configuré le JWT pour avoir une expiration de 60 secondes. C’est probablement une expiration trop courte, et traiter les détails de l’expiration et du rafraîchissement de token dépasse le cadre de cet article. Cependant, nous avons choisi cela pour démontrer une qualité importante des JWT et de la stratégie passport-jwt. Si vous attendez 60 secondes après l’authentification avant de tenter une demande GET /profile
, vous recevrez une réponse 401 Unauthorized
. Cela est dû au fait que Passport vérifie automatiquement le JWT pour son heure d’expiration.
Nous avons maintenant terminé notre mise en œuvre d’authentification JWT. Les clients JavaScript (tels que Angular/React/Vue) et d’autres applications JavaScript peuvent maintenant s’authentifier et communiquer en toute sécurité avec notre serveur API.
Étendre les guards
Dans la plupart des cas, l’utilisation d’une classe AuthGuard
fournie est suffisante. Cependant, il peut y avoir des cas d’utilisation où vous souhaiteriez simplement étendre la gestion des erreurs par défaut ou la logique d’authentification. Pour cela, vous pouvez étendre la classe intégrée et remplacer les méthodes dans une sous-classe.
import { ExecutionContext, Injectable, UnauthorizedException,} from '@nestjs/common';import { AuthGuard } from '@nestjs/passport';
@Injectable()export class JwtAuthGuard extends AuthGuard('jwt') { canActivate(context: ExecutionContext) { return super.canActivate(context); }
handleRequest(err, user, info) { if (err || !user) { throw err || new UnauthorizedException(); } return user; }}
Résumé
Nous avons couvert une grande variété de techniques et de stratégies pour mettre en œuvre l’authentification dans une application NestJS à l’aide de Passport. En appliquant ces concepts, vous pouvez adapter le système d’authentification à vos besoins spécifiques tout en garantissant sécurité et facilité d’utilisation.