JWT PASPORT
Actualizar tokens¶
¿Cómo puedo implementar la expiración de tokens y la actualización de tokens en Nest JS usando Passport JS?¶
La implementación de la expiración de tokens y la actualización de tokens en NestJS usando Passport.js implica varios pasos. Aquí hay una guía para ayudarlo a comenzar:
-
Instalar los paquetes necesarios:
npm install @nestjs/passport passport passport-jwt jsonwebtoken -
Crear una estrategia JWT: Crear un archivo
jwt.strategy.tsy agregar el siguiente código:import { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; import { jwtConstants } from './constants'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor() { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: jwtConstants.secret, }); } async validation(payload: any) { return { userId: payload.sub, username: payload.username }; } } -
Crear un archivo de constantes: Crear un archivo
constants.tsy agregar el siguiente código:export const jwtConstants = { secret: 'yourSecretKey', }; -
Crear un AuthService: Crear un archivo
auth.service.tsy agregar el siguiente código:import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; @Injectable() export class AuthService { constructor(private readonly jwtService: JwtService) {} async generateAccessToken(user: any) { const payload = { username: user.username, sub: user.userId }; return this.jwtService.sign(payload, { expiresIn: '15m' }); } async generateRefreshToken(user: any) { const payload = { username: user.username, sub: user.userId }; return this.jwtService.sign(payload, { expiresIn: '7d' }); } async refreshTokens(refreshToken: string) { try { const decoded = this.jwtService.verify(refreshToken, { secret: jwtConstants.secret }); const user = { userId: decoded.sub, username: decoded.username }; return { accessToken: await this.generateAccessToken(usuario), refreshToken: await this.generateRefreshToken(usuario), }; } catch (err) { throw new Error('Token de actualización no válido'); } } } -
Crear un AuthController: Crear un archivo
auth.controller.tsy agregar el siguiente código:import { Controller, Post, Body, UseGuards, Request } from '@nestjs/common'; import { AuthService } from './auth.service'; import { JwtAuthGuard } from './jwt-auth.guard'; @Controller('auth') export class AuthController { constructor(private readonly authService: AuthService) {} @Post('login') async login(@Body() user: any) { const accessToken = await this.authService.generateAccessToken(user); const refreshToken = await this.authService.generateRefreshToken(user); return { accessToken, refreshToken }; } @UseGuards(JwtAuthGuard) @Post('refresh') async refresh(@Request() req) { const { refreshToken } = req.body; return this.authService.refreshTokens(refreshToken); } } -
Crear un JWT Auth Guard: Crear un archivo
jwt-auth.guard.tsy agregar el siguiente código:import { Injectable } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; @Injectable() export class JwtAuthGuard extends AuthGuard('jwt') {} -
Actualice AppModule: Actualice su
app.module.tspara incluir las importaciones y los proveedores necesarios:import { Module } from '@nestjs/common'; import { JwtModule } from '@nestjs/jwt'; import { PassportModule } from '@nestjs/passport'; import { AuthService } from './auth.service'; import { AuthController } from './auth.controller'; import { JwtStrategy } from './jwt.strategy'; import { jwtConstants } from './constants'; @Module({ imports: [ PassportModule, JwtModule.register({ secret: jwtConstants.secret, signOptions: { expiresIn: '15m' }, }), ], providers: [AuthService, JwtStrategy], controllers: [AuthController], }) export class AppModule {}
¿Cómo puedo validar el token y actualizarlo, en caso de que el usuario cierre la sesión?¶
Cuando un usuario cierra la sesión, querrás invalidar sus tokens existentes para asegurarte de que ya no se puedan usar. Un enfoque común es usar una lista negra de tokens o almacenar una lista de tokens invalidados.
A continuación, se muestra cómo puede hacerlo en NestJS:
-
Instalar Redis: Redis se puede usar para almacenar tokens invalidados. Instale los paquetes necesarios:
npm install redis @nestjs/microservices @nestjs/microservices/package redis -
Crear un TokenService: Cree un archivo
token.service.tsy agregue el siguiente código:import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import * as redis from 'redis'; @Injectable() export class TokenService { private readonly redisClient; constructor(private readonly jwtService: JwtService) { this.redisClient = redis.createClient(); } async invalidateToken(token: string): Promise<void> { const decoded = this.jwtService.decode(token); const expiry = decoded['exp']; const now = Math.floor(Date.now() / 1000); if (expiry > now) { await this.redisClient.set(token, 'invalid', 'EX', expiry - now); } } async isTokenInvalid(token: string): Promise<boolean> { return new Promise((resolve, reject) => { this.redisClient.get(token, (err, result) => { if (err) { reject(err); } else { solve(result === 'invalid'); } } }); } } -
Actualice AuthService para manejar la invalidación de tokens: Actualice su
``tspara incluir la lógica de invalidación de tokens:import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { TokenService } from './token.service'; @Injectable() export class AuthService { constructor( private readonly jwtService: JwtService, private readonly tokenService: TokenService, ) {} async generateAccessToken(usuario: cualquiera) { const payload = { nombre_usuario: usuario.nombre_usuario, sub: usuario.Id_usuario }; return this.jwtService.sign(payload, { expiresIn: '15m' }); } async generateRefreshToken(usuario: cualquiera) { const payload = { nombre_usuario: usuario.nombre_usuario, sub: usuario.Id_usuario }; return this.jwtService.sign(payload, { expiresIn: '7d' }); } async refreshTokens(refreshToken: string) { const isInvalid = await this.tokenService.isTokenInvalid(refreshToken); if (isInvalid) { throw new Error('Token de actualización no válido'); } const decoded = this.jwtService.verify(refreshToken, { secret: 'yourSecretKey' }); const user = { userId: decoded.sub, username: decoded.username }; return { accessToken: await this.generateAccessToken(usuario), refreshToken: await this.generateRefreshToken(usuario), }; } async logout(token: string) { await this.tokenService.invalidateToken(token); } } -
Actualice AuthController para manejar la invalidación de tokens: Actualice su
auth.controller.tspara incluir un punto final de cierre de sesión:import { Controller, Post, Body, UseGuards, Request } from '@nestjs/common'; import { AuthService } from './auth.service'; import { JwtAuthGuard } from './jwt-auth.guard'; @Controller('auth') export class AuthController { constructor(private readonly authService: AuthService) {} @Post('login') async login(@Body() user: any) { const accessToken = await this.authService.generateAccessToken(user); const refreshToken = await this.authService.generateRefreshToken(user); return { accessToken, refreshToken }; } @UseGuards(JwtAuthGuard) @Post('refresh') async refresh(@Request() req) { const { refreshToken } = req.body; return this.authService.refreshTokens(refreshToken); } @UseGuards(JwtAuthGuard) @Post('logout') async logout(@Request() req) { const { token } = req.body; await this.authService.logout(token); return { message: 'Se cerró sesión correctamente' }; } }
Explicación:
- TokenService: este servicio se encarga de invalidar tokens y de verificar si un token es inválido. Redis se utiliza para almacenar tokens invalidados. 2. AuthService: Se agregó el método
logoutpara invalidar tokens. El métodorefreshTokensverifica si el token de actualización no es válido antes de generar nuevos tokens. - AuthController: Se agregó un nuevo punto final
logoutpara manejar solicitudes de cierre de sesión e invalidar tokens.