¿Cómo implementar la generación de un token en una API?
Implementar la generación de tokens en tu API es esencial para asegurar la autenticación y autorización de los usuarios. En este contexto, al usuario, una vez autenticado, se le debe otorgar un token que permita manejar su sesión y conectarse a otros endpoints. Veamos cómo implementar esta lógica utilizando JSON Web Tokens (JWT).
¿Cómo generar un token?
Importación de librerías: Comienza importando la librería de JSON Web Token. Esto es crucial ya que te permitirá trabajar con JWT en tu aplicación.
const jwt =require('jsonwebtoken');
Definir el payload: El payload contiene la información que se almacenará en el token. Esto puede incluir cualquier dato relevante, como el ID del usuario. En nuestro caso, utilizaremos el ID y el rol del usuario.
Generar el token: Una vez que se tiene el payload, se genera el token utilizando una clave secreta que no debe estar en el código fuente, sino configurada como una variable de entorno.
Es importante que el secreto utilizado para firmar el token sea seguro y esté gestionado como una variable de entorno. Esto lo proporciona mayor seguridad frente a ataques. Aquí te explicamos cómo:
Crear una variable de entorno: Define una variable de entorno llamada JWT_SECRET que contenga el secreto.
Ejemplo en .env:
JWT_SECRET=your-secret-key
Generación del secreto: Utiliza herramientas en línea como Keygen.io para generar claves seguras mediante algoritmos robustos.
¿Cómo reiniciar el entorno de desarrollo?
Después de configurar las variables de entorno, es esencial reiniciar tu entorno de desarrollo para que los cambios surtan efecto. Si no lo haces, podrías enfrentar errores, como el que el secreto no tenga un valor asignado.
Detener la aplicación: Si tu aplicación está corriendo, asegúrate de detenerla antes de realizar cambios.
Reiniciar la aplicación: Una vez configurada la variable de entorno, corre nuevamente tu aplicación para cargar los nuevos valores.
¿Qué esperar al probar el login?
Una vez implementada la lógica de generación de tokens, al probar el endpoint de login, se espera lo siguiente:
Respuesta: El sistema debería devolver tanto la información del usuario como el token recién generado.
Funcionalidad segura: Este token le permitirá al usuario manejar su sesión de manera segura en los siguientes requests.
Por último, asegúrate de aplicar estos tokens en la protección de las rutas de tu API, forzando a los usuarios a enviar el token para acceder a recursos protegidos. ¡Así garantizas una capa más de seguridad y control en tus servicios!
First of all, configure .env to provide a secret and it won't be accessible from code.
jwtSecret="A SECRET REALLY SECRET"
Page to generate greats passwords:
In our auth.routes configure secret such as we did in the last lesson
router.post('/login',//We are using the local strategy and not using sessions passport.authenticate('local',{session:false}),async(req, res, next)=>{try{const user = req.user;**const payload ={sub: user.id,role: user.role}****const token = jwt.sign(payload, config.auth.jwtSecret);** res.json({ user, token });}catch(err){next(err);}});
Finally,
try it in insomnia/postman!
thanks for the summary
just for feedback, it sounds weird 'a secret really secret'. I'm sure it's supposed to be 'a really secret secret'
Al implementar JWT ya no es necesario enviar los datos del usuario en la petición, ya que por medio del payload del token podemos enviarla, ademas recordar que por ningún motivo se debe enviar informacion sensible del usuario.
En el archivo auth.route.js se hace la implementación del JWT, primero se hace la autenticación con la estrategia local y con ello se tiene al usuario (req.user), por lo tanto, se crea el payload con los datos del usuario, posteriormente se firma el token con la función .sign() y como respuesta se envía un JSON con el user y el token.
Otra cosa que es recomendable agregarle al token es un tiempo de expiración (también definido desde envs o alguna configuración por usuario/cliente)
const token = jwt.sign(payload,"secret",{// Ejemplo: token expira en 10 horasexpiresIn:"10h"})
Otra forma de generar secrets desde la terminal es:
$ openssl rand -hex 45
🗝 Clase #10: Generar JWT en el servicio 10/20 🗝
Pasos: 📝
Vamos a implementar la lógica para que cuando se haga login, tengamos el token. Para ello en VSC, en la carpeta routes, abrimos el archivo auth.router.js, el código queda:
const express =require('express');const passport =require('passport');const jwt =require('jsonwebtoken');//Traemos el config para el jwtSecretconst{ config }=require('./../config/config');const router = express.Router();router.post('/login', passport.authenticate('local',{session:false}),//Al pasar la capa de autenticación después de verificar que el usuario y password son correctos//al devolver el login, se le brinda un tokenasync(req, res, next)=>{try{const user = req.user;const payload ={sub: user.id,role: user.role}//Se hace la firmaconst token = jwt.sign(payload, config.jwtSecret); res.json({ user, token
});}catch(error){next(error);}});module.exports= router;
Guardamos, vamos a la carpeta config y abrimos el archivo config.js y después de apiKey colocamos:
jwtSecret: process.env.JWT_SECRET,
Guardamos, abrimos la página para generar claves (enlace: aquí), para ésta clase se buscó: WEP 256-bit Key, se generó en la pestaña ++NEW++ y luego al dar en la pestaña ++COPY++, aparece un cuadro donde indica que al presionar las teclas Ctrl + C se copia la clave generada.
Esa clave se copia y se pega en el archivo de variables de entorno .env después de API_KEY se coloca el JWT_SECRET, para mi ejemplo:
JWT_SECRET=csvV9KYB4QeqhaEgoJdIzwunROb1XDZU
Guardamos, como se modificó el archivo .env con las variables de entorno, en la terminal se corre de nuevo:
npm run dev
Vamos a Insomia y en la carpeta de Auth en Login, la solicitud del ++POST++ con la dirección: _.API_URL/api/v1/auth/login
Para aplicar el middleware de inicio de sesión dentro de una ruta en Express, puedes utilizar la función app.use() para aplicar el middleware de autenticación a rutas específicas. Aquí hay un ejemplo básico:
const express =require('express');const app =express();// Middleware de autenticaciónfunctionisAuthenticated(req, res, next){if(req.isAuthenticated()){returnnext();} res.redirect('/login');// Redirige a la página de inicio de sesión si no está autenticado}// Ruta protegidaapp.get('/dashboard', isAuthenticated,(req, res)=>{ res.send('Bienvenido al panel de control');});
En este ejemplo, isAuthenticated es el middleware que verifica si el usuario está autenticado antes de permitir el acceso a la ruta /dashboard. Puedes modificarlo según tus necesidades.
Profesor @nicobyte Nicolás Molina, ¿puede ser una buena opción, encriptar la información del payload antes de ingresarla, por ejemplo el correo?
¡Uhh, qué buena pregunta! 🤔
Me dejaste pensando también. Según lo que investigué y creo, encriptar la información sensible en el payload de un JWT puede ser útil para evitar comprometer esos datos. Sin embargo, esto añade un grado mayor de complejidad, ya que necesitarías implementar un mecanismo para desencriptar el payload antes de interactuar con la base de datos
¿Cómo se implementaría si quiero que el token que le generamos al usuario se guarde en los headers y no estemos copiando y pegando el token a cada rato?
En insomnia lo que se me ocurre es guardarlo como variable de ambiente como hicimos con la URL
Otra opcion para generar los secrets es directamente con el mismo node creando caracteres aleatorios, en este caso lo hago con un oneliner desde la terminal