A tu propio ritmo

Explora nuestra extensa colección de cursos diseñados para ayudarte a dominar varios temas y habilidades. Ya seas un principiante o un aprendiz avanzado, aquí hay algo para todos.

Bootcamp

Aprende en vivo

Únete a nosotros en nuestros talleres gratuitos, webinars y otros eventos para aprender más sobre nuestros programas y comenzar tu camino para convertirte en desarrollador.

Próximos eventos en vivo

Catálogo de contenidos

Para los geeks autodidactas, este es nuestro extenso catálogo de contenido con todos los materiales y tutoriales que hemos desarrollado hasta el día de hoy.

Tiene sentido comenzar a aprender leyendo y viendo videos sobre los fundamentos y cómo funcionan las cosas.

Buscar en lecciones


IngresarEmpezar
← Regresar a lecciones
  • APIs

  • Express

  • HTTP

  • Security

  • Authentication

  • typeOrm

Editar en Github

Comprendiendo JWT y como implementar un JWT simple con Express

JSON Web Tokens (JWT) para la Autenticación de APIs

JSON Web Tokens (JWT) para la Autenticación de APIs

En el mundo del desarrollo web moderno, la autenticación y autorización de usuarios son aspectos cruciales para proteger las APIs y los datos sensibles. Entre las soluciones más populares se encuentran los JSON Web Tokens (JWT), un estándar abierto y ligero que define un mecanismo compacto y autosuficiente para la transmisión segura de información entre partes como cliente y servidor.

¿Qué son los JWT?

Un JWT es un token que contiene información sobre la identidad de un usuario y la autorización para acceder a recursos específicos. Se compone de tres partes:

☝️ Si no sabes lo que es un token, te recomiendo esta lectura.

Encabezado (Header): Especifica el tipo de token, el algoritmo de firma y otra información relevante.

Carga útil (Payload): Contiene los datos sobre el usuario, como la identificación, nombre, roles y fecha de expiración del token.

Firma: Garantiza la integridad y autenticidad del token mediante una firma digital utilizando un algoritmo criptográfico y una clave secreta compartida entre el servidor y el cliente.

¿Cómo funciona JWT en la autenticación de APIs?

El esquema a implementar en este caso puede resumirse en la siguiente imagen

Autentication workflow

  1. Inicio de sesión: El usuario ingresa sus credenciales (correo electrónico y contraseña) en el cliente.

  2. Autenticación: El servidor valida las credenciales del usuario y, si son correctas, genera un JWT con la información del usuario y lo firma con su clave secreta.

  3. Envío del token: El servidor envía el JWT al cliente como respuesta a la solicitud de inicio de sesión.

  4. Almacenamiento del token: El cliente almacena el JWT de forma segura, generalmente en el almacenamiento local o en una cookie HTTP.

  5. Solicitudes posteriores: En cada solicitud posterior a la API, el cliente incluye el JWT en el encabezado de autorización.

  6. Validación del token: El servidor verifica la firma del JWT para asegurar su autenticidad e integridad. Si el token es válido, extrae la información del usuario de la carga útil y la utiliza para autorizar el acceso al recurso solicitado.

Implementación de JWT en la API de tu proyecto

1) Instalación

Instala estas 3 librerías que se encargarán de generar los tokens JWT:

1npm install express-jwt @types/express-jwt jsonwebtoken @types/jsonwebtoken --save

2) Login endpoint

El segundo paso es crear una ruta API que pueda ser llamada por el cliente para generar un token (también conocido como login), esta ruta recibirá el email y password del body y buscará cualquier usuario en la base de datos que coincida con esos dos valores.

Si encuentra el valor, generará un token llamando a la función jwt.sign.

1//esta línea va en tu public_routes.ts 2router.post('/token', safe(createToken)); 3 4// esta función va en tu actions.ts 5import jwt from 'jsonwebtoken' 6 7export const createToken = async (req: Request, res: Response): Promise<Response> =>{ 8 9 if(!req.body.email) throw new Exception("Please specify an email on your request body", 400) 10 if(!req.body.password) throw new Exception("Please specify a password on your request body", 400) 11 12 const userRepo = await getRepository(Users) 13 14 // Necesitamos validar que existe un usuario con este email y contraseña en la BD 15 const user = await userRepo.findOne({ where: { email: req.body.email, password: req.body.password }}) 16 if(!user) throw new Exception("Invalid email or password", 401) 17 18 // esta es la línea más importante en esta función, se crea un token JWT 19 const token = jwt.sign({ user }, process.env.JWT_KEY as string); 20 21 // devolver al cliente el usuario y el token creado recientemente 22 return res.json({ user, token }); 23}

3) Validación de peticiones con JWT

Ahora tenemos que añadir un middleware que buscará el token en el Encabezado de autorización de la petición. El middleware interceptará cada petición y ejecuta la función next para avanzar solo si logra validar el token, en caso contrario retornará un error.

Añade estos dos middlewares dentro de ./src/app.js que se encargarán de hacer cumplir el token.

1// ⬆ todo lo ANTERIOR es público 2import jwt, { Options } from 'express-jwt'; 3 4let opt: Options = { secret: process.env.JWT_KEY as string, algorithms: ["HS256"] } 5app.use(jwt(opt)) 6// ⬇ todo lo que esté POR DEBAJO es público 7app.use(((err: any, req: any, res: any, next: any) => { 8 if (err) console.error(err); 9 if (err.name === 'UnauthorizedError') { 10 return res.status(401).json({ status: err.message }); 11 } 12 next(); 13}))

⚠️ Important

Cualquier endpoint que se añada DEBAJO de estos middlewares será privado, por ejemplo:

1app.get('/public', (req, res) => { 2 res.json({ message: "Anyone can see me" }); 3}) 4 5// ⬆ anything ABOVE is public 6app.use(jwt(opt)) // ⬅ JWT Middleware 7// ⬇ anything BELOW is public 8 9app.get('/private', (req, res) => { 10 res.json({ message: "If you can se me, you are logged in" }); 11})

3) Obtener el usuario autenticado

Hemos terminado, pero si sólo los usuarios registrados pueden llamar a nuestros endpoints privados, entonces necesitamos una forma de saber quién los está llamando, por ejemplo podemos usar req.user de ahora en adelante, para identificar al usuario de la petición:

1export const getMe = async (req: Request, res: Response): Promise<Response> =>{ 2 3 const users = await getRepository(Users).find({ where: }); 4 // ⬇ no procedentes de la BD 5 return res.json(req.user); 6}

O podemos utilizar esa información y obtener más información del solicitante de la base de datos.

1export const getMe = async (req: Request, res: Response): Promise<Response> =>{ 2 3 4 // ⬇ not comming from the BD 5 return res.json(req.user); 6}

Implementando JWT en tu proyecto Front-End

En el lado del front-end necesitamos dos pasos principales: Crear un nuevo token (también conocido como "login") y añadir el token a los headers cuando se obtenga cualquier otro endpoint privado.

Crear un nuevo token

Basándonos en los endpoints que construimos anteriormente, tenemos que hacer POST /token con la información del nombre de usuario y la contraseña en el body de la petición.

1const login = async (username, password) => { 2 const resp = await fetch(`https://your_api.com/token`, { 3 method: "POST", 4 headers: { "Content-Type": "application/json" }, 5 body: JSON.stringify({ username, password }) 6 }) 7 8 if(!resp.ok) throw Error("There was a problem in the login request") 9 10 if(resp.status === 401){ 11 throw("Invalid credentials") 12 } 13 else if(resp.status === 400){ 14 throw ("Invalid email or password format") 15 } 16 const data = await resp.json() 17 // Guarda el token en la localStorage 18 // También deberías almacenar el usuario en la store utilizando la función setItem 19 localStorage.setItem("jwt-token", data.token); 20 21 return data 22}

Obteniendo cualquier información privada

Supongamos que estoy usando la aplicación de front-end y acabo de iniciar sesión, pero ahora quiero obtener algún endpoint privado o protegido:

1// Asumiendo que "/protected" es un endpoint privado 2const getMyTasks = async () => { 3 // Recupera el token desde la localStorage 4 const token = localStorage.getItem('jwt-token'); 5 6 const resp = await fetch(`https://your_api.com/protected`, { 7 method: 'GET', 8 headers: { 9 "Content-Type": "application/json", 10 'Authorization': 'Bearer ' + token // ⬅⬅⬅ authorization token 11 } 12 }); 13 14 if(!resp.ok) { 15 throw Error("There was a problem in the login request") 16 } else if(resp.status === 403) { 17 throw Error("Missing or invalid token"); 18 } else { 19 throw Error("Unknown error"); 20 } 21 22 const data = await resp.json(); 23 console.log("This is the data you requested", data); 24 return data 25}

¡Eso es todo! Como puedes ver, es muy sencillo integrar JWT en tu aplicación usando Express, solo cuatro pasos en el backend y dos pasos en el frontend. Ante cualquier duda puedes contactarme en Twitter @alesanchezr o utilizar el canal #public-support en la comunidad Slack de 4Geeks Academy.