Skip to content

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:

  1. Instalar los paquetes necesarios:

    npm install @nestjs/passport passport passport-jwt jsonwebtoken
    

  2. Crear una estrategia JWT: Crear un archivo jwt.strategy.ts y 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 };
        }
    }
    

  3. Crear un archivo de constantes: Crear un archivo constants.ts y agregar el siguiente código:

    export const jwtConstants = {
        secret: 'yourSecretKey',
    };
    

  4. Crear un AuthService: Crear un archivo auth.service.ts y 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');
            }
        }
    }
    

  5. Crear un AuthController: Crear un archivo auth.controller.ts y 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);
        }
    }
    

  6. Crear un JWT Auth Guard: Crear un archivo jwt-auth.guard.ts y agregar el siguiente código:

    import { Injectable } from '@nestjs/common';
    import { AuthGuard } from '@nestjs/passport';
    
    @Injectable()
    export class JwtAuthGuard extends AuthGuard('jwt') {}
    

  7. Actualice AppModule: Actualice su app.module.ts para 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:

  1. Instalar Redis: Redis se puede usar para almacenar tokens invalidados. Instale los paquetes necesarios:

    npm install redis @nestjs/microservices @nestjs/microservices/package redis
    

  2. Crear un TokenService: Cree un archivo token.service.ts y 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');
                    }
                }
            });
        }
    }
    

  3. Actualice AuthService para manejar la invalidación de tokens: Actualice su ``ts para 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);
        }
    }
    

  4. Actualice AuthController para manejar la invalidación de tokens: Actualice su auth.controller.ts para 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:

  1. 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 logout para invalidar tokens. El método refreshTokens verifica si el token de actualización no es válido antes de generar nuevos tokens.
  2. AuthController: Se agregó un nuevo punto final logout para manejar solicitudes de cierre de sesión e invalidar tokens.