Firmar tokens de forma segura y controlar su expiración es una pieza fundamental en cualquier API que maneje autenticación. Aquí se aborda paso a paso cómo registrar el JWT Module dentro del módulo de autenticación en NestJS, proteger el secret con variables de entorno y, finalmente, generar un access token funcional desde el flujo de login.
¿Por qué es necesario registrar el JWT Module en el auth module?
En la clase anterior se instalaron las dependencias @nestjs/jwt y se creó el método generateJWT dentro del AuthService. Sin embargo, el JwtService que ese método utiliza no puede resolverse si el módulo correspondiente no está importado. Al correr la aplicación sin esta configuración, NestJS lanza un error de dependencia.
Para solucionarlo hay que agregar JwtModule en el array de imports del AuthModule [01:07]. Con la programación modular de NestJS, al importar un módulo se exponen sus servicios internos —en este caso JwtService— para que puedan inyectarse donde se necesiten.
¿Qué representa el secret y por qué debe estar protegido?
El secret funciona como la llave de una caja fuerte [00:40]. Solo quien posea esa llave puede descifrar la información contenida en el token. Si alguien con acceso al repositorio encuentra el secret escrito de forma explícita en el código, podría desencriptar cualquier token y suplantar usuarios.
- El secret es una cadena (palabra, frase o valor aleatorio) con la que se firman todos los JWT.
- Debe almacenarse en una variable de entorno, nunca hard-coded.
- Solo el responsable de la infraestructura o del deployment a producción debería conocer la llave real.
¿Cómo se configura la expiración del token?
Al registrar el módulo se pasa un objeto con signOptions que incluye expiresIn [03:00]. En el ejemplo se utiliza 10d (diez días), lo que significa que pasado ese tiempo el token deja de ser válido y el usuario debe autenticarse de nuevo.
- Existen estrategias más robustas como manejar la expiración en segundos y combinarla con un refresh token.
- Con un refresh token se puede renovar el access token de forma transparente para el usuario.
- Para profundizar en estas prácticas de seguridad se recomienda el curso de Passport de Node en Platzi.
¿Cómo leer el secret desde variables de entorno con registerAsync?
En lugar de JwtModule.register(), se utiliza JwtModule.registerAsync() [04:28]. Este método permite inyectar dependencias —como el ConfigService— mediante un useFactory.
typescript
JwtModule.registerAsync({
inject: [config.KEY],
useFactory: (configService: ConfigType<typeof config>) => {
return {
secret: configService.jwtSecret,
signOptions: {
expiresIn: '10d',
},
};
},
})
Pasos clave en esta configuración:
- Se agrega
JWT_SECRET al archivo .env y se declara en el esquema de configuración [03:47].
- Se marca como requerida en la validación del
AppModule para que el proyecto no arranque sin ella [04:15].
- Se inyecta
config con ConfigType y typeof config para obtener tipado seguro.
¿Dónde se invoca el método generateJWT?
El método generateJWT del AuthService recibe el usuario validado, selecciona los datos que viajarán dentro del payload (como el rol y el id), firma el token con JwtService.sign() y devuelve un objeto con el access token y la información del usuario [06:40].
Este método se llama en el AuthController, justo después de que el guard de Passport confirma que el email y password son correctos [07:05].
typescript
@UseGuards(AuthGuard('local'))
@Post('login')
login(@Req() req: Request) {
const user = req.user as User;
return this.authService.generateJWT(user);
}
¿Por qué se necesita el casteo as User?
Express tipifica req.user de forma genérica. Al hacer un casteo hacia la entidad User propia del proyecto, se garantiza que TypeScript reconozca las propiedades disponibles (rol, id, email) y evite errores en tiempo de compilación [08:10]. Usar any resolvería el error, pero se perdería la seguridad de tipos.
¿Cómo verificar que el token se genera correctamente?
Al enviar una petición POST a /login con credenciales válidas desde Insomnia, la respuesta incluye el objeto del usuario junto con el access token [09:22]. Un JWT válido se compone de tres partes separadas por puntos: el header, el payload y la signature.
Con el token generado, el siguiente paso será proteger los demás endpoints para que validen este token en cada petición, comprueben que no haya expirado y concedan o nieguen el acceso según corresponda.
¿Ya lograste generar tu primer token? Comparte en los comentarios qué estrategia de expiración planeas usar en tu proyecto.