¿Cómo asegurar la comunicación entre APIs utilizando tokens de acceso?
Los tokens de acceso son una pieza clave para mantener la seguridad en la comunicación entre APIs y clientes. En este contexto, una de las técnicas utilizadas es la implementación de estrategias de autenticación basadas en JWT (JSON Web Token), permitiendo validar la identidad y los permisos de los usuarios.
¿Cómo crear una estrategia para validar JWT?
Para garantizar que cada request a nuestra API esté debidamente autentificado, creamos una strategy que se encargue de validar el JWT. Esta se asegurará de que el token no haya expirado y sea válido, utilizando la siguiente estructura:
Creación de la estrategia: Utilizamos una JWT strategy que verificará la integridad del token. Esto involucra configurar parámetros como desde dónde se tomará el token (habitualmente desde el header) y asegurarse de que el token sea un Bearer token.
Configurar el guardián: Al crear un guardián (guard), podemos proteger ciertos endpoints exigendo la presencia de un token válido. Esto asegura que solo requests autorizados accedan a los datos o servicios:
¿Cómo implementar la validación de tokens en los endpoints?
Una vez implementada la strategy, es inmediata la aplicación sobre los endpoints que requerimos proteger:
Proteger rutas específicas: Al estar una ruta protegida, requiere que el request venga acompañado de un token válido. Los clientes de la API deben asegurarse de enviar este token en cada solicitud.
Validar tokens en cada solicitud: Es fundamental que cada endpoint verifique el token, lo cual asegura que el acceso sea controlado y se restrinja solo a usuarios autorizados, limitando brechas de seguridad.
¿Qué ventajas ofrece la tokenización de requests en APIs?
El uso de tokens no solo eleva el nivel de seguridad, sino que también ofrece otras ventajas prácticas:
Autenticación centralizada: Facilita la gestión de roles y permisos desde un único punto, favoreciendo la escalabilidad.
Integración sencilla con múltiples plataformas: Clientes web, móviles, o incluso dispositivos IoT pueden incorporar fácilmente el token, garantizando autenticidad desde diversos entornos.
Control granular de acceso: Permite definir con precisión qué acciones puede llevar a cabo un usuario, mejorando la gestión de recursos y privilegios.
Adoptar estos mecanismos no solo robustece la seguridad de nuestras aplicaciones, sino que también mejora la confiabilidad del sistema, proporcionando un entorno seguro y eficiente para los usuarios. Este camino fomenta el aprendizaje continuo, instando a desarrolladores a explorar más a fondo las herramientas a su disposición para construir aplicaciones seguras y escalables.
Estrategia y Guardia de Autenticación con JWT en NestJS
Estrategia y Guardia de Autenticación con JWT en NestJS
SE TE COMPLICO?
DEJA TE HECHO UNA MANITA
1.- Primero creamos el archivo jwt.strategy.ts en el cual agregaremos lo siguiente
import{ExtractJwt,Strategy}from'passport-jwt';import{PassportStrategy}from'@nestjs/passport';import{Injectable}from'@nestjs/common';import{ jwtConstants }from'./constants';import{TokenPayloadModel}from'./models/token.model';@Injectable()exportclassJwtStrategyextendsPassportStrategy(Strategy){constructor(){super({jwtFromRequest:ExtractJwt.fromAuthHeaderAsBearerToken(),// OBTENDREMOS EL TOKEN LOS HEADERS COMO 'Bearer token'ignoreExpiration:false,// IGNORA LA EXPIRACION, EN TU CASO EL TIEMPO QUE LE HAYAS PUESTO// EJE. signOptions: { expiresIn: '24h' }, YO LE PUSE 1 DIA secretOrKey: jwtConstants.secret,// LA LLAVE SECRETA CON LA QUE FIRMAMOS EL TOKEN AL HACER LOGIN});}// ESTA FUNCION LO QUE HARA SERA RECIBIR EL TOKEN DECODIFICADO // CON LA CARGA DE DATOS QUE LE PUSIMOS AL HACER LOGINasyncvalidate(payload:TokenPayloadModel){return payload;}}
LA FUNCION VALIDATE ES IMPORTANTE YA QUE SI EL TOKEN ES VALIDO POR NUESTRO SERVIDOR, ESTE RETURN DE LA FUNCION MISMA DEVOLVERA EL PAYLOAD AL REQUEST DE NUESTRA RUTA A LA QUE VAYAMOS A PROTEGER, ESTO TE PUEDE SERVIR PARA A BUSCAR EN TU BD AL USUARIO O ALGUN PROCESO QUE REQUIERAS
2.- Lo siguiente es importar nuestra estrategia en nuestro modulo de autenticación en los providers
3.- Ya casi acabamos!, solo falta crear el guardián que protegerá nuestrs rutas, creamos un archivo llamado "jwt.auth.guard.ts" dentro de nuestor modulo de autenticacion
import{HttpException,HttpStatus,Injectable,UnauthorizedException}from'@nestjs/common';import{AuthGuard}from'@nestjs/passport';@Injectable()exportclassJwtAuthGuardextendsAuthGuard('jwt'){// CON ESTA FUNCION DE PODEMOS PERSONALIZAR SI // QUEREMOS LANZAR ERRORES PERZONALIZADOS handleRequest(err, user, info){if(err ||!user){thrownewHttpException({status:HttpStatus.UNAUTHORIZED,error:'Usuario no autorizado',},HttpStatus.UNAUTHORIZED);}return user;}}
4.- Por ultimo solo importamos nuestro guardián en nuestro controlador que queramos proteger
Yo usare mi controlador llamado Pages
y listo, nuestra ruta quedo protegida, para acceder a ella abría que enviar por los headers nuestro token
Dejo un aporte para los que usen postman. Validar que venga el token y agregarlo a una variable.
pm.test("Response data check", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.access_token).to.exist;
});
pm.environment.set("token", pm.response.json().access_token);
El Bearer token en un cliente de promesas como Axios, ¿Cómo se llama? Entiendo que sería un campo en el request, pero ¿De qué manera sería?
Implementando JWT Guard
Ya logramos crear nuestra estrategia de login de usuario, ahora, veamos como podemos implementarla en nuestros diferentes Endpoints.
Para hacer esto, lo que tenemos que hacer es crear una estrategia que verifique el token en cada Endpoint. Es más, en base a este token vamos a poder saber desde el backend que usuarios están registrados, y esto lo sabremos en base al token.
src/auth/strategies/jwt.strategy.ts:
import{Injectable,Inject}from'@nestjs/common';// nos traemos el paquete de configuración de Nestimport{ConfigType}from'@nestjs/config';// importamos el tipado del tokenimport{PayloadToken}from'../models/token.model';// importamos la estrategia para extender la claseimport{PassportStrategy}from'@nestjs/passport';// importamos la estrategía de "passport-jwt"// "ExtractJwt" es el método por donde vamos a obtener el tokenimport{Strategy,ExtractJwt}from'passport-jwt';// nos traemos el configimportconfigfrom'src/config';@Injectable()exportclassJwtStrategyextendsPassportStrategy(Strategy,'jwt'){constructor(// inyectamos el servicio de configuración en la estrategía @Inject(config.KEY)private configService:ConfigType<typeof config>,){// en nuestro constructor, en su super, añadimos las siguientes variables de configuraciónsuper({// desde donde va a capturar el token// lo capturará desde el header// con el formato de "bearer token" jwtFromRequest:ExtractJwt.fromAuthHeaderAsBearerToken(),// no queremos ignorar la expiración del token ignoreExpiration:false,// el enviamos cual es el secreto para que pueda desencriptar el token secretOrKey: configService.jwtSecret,});}// creamos el método para validar el usuariovalidate(payload:PayloadToken){return payload;}}
Bien, ya tenemos nuestra estrategia, ahora, debemos importarla en el el módulo de autenticación.
**src/auth/auth.module.ts**:
// ...// importamos la nueva estrategiaimport{JwtStrategy}from'./strategies/jwt.strategy';@Module({// ...// la agregamos en el provider providers:[...JwtStrategy],})exportclassAuthModule{}
Bien, ahora podríamos utilizarlo directamente en uno de los servicios que deseemos, digamos que queremos tener un usuario autenticado, para que pueda usar los Endpoints de productos:
// importamos "UseGuards"import{UseGuards}from'@nestjs/common';// importamos al "AuthGuard" de Passportimport{AuthGuard}from'@nestjs/passport';// usamos la estrategia de autenticación de jwt en todos los endpoints de usuarios@UseGuards(AuthGuard('jwt'))@ApiTags('products')@Controller('products')exportclassProductsController{// ...}
Ahora, si vamos a hacer un Request en cualquier Endpoint de usuarios, debemos enviar el token, en Postman se vería algo así:
!Untitled
Y listo, si nuestro token es un token correcto, nos debería permitir usar los Endpoints, de lo contrario, no nos va a permitir usar los Endpoints de Products.