En aplicaciones modernas, como aquellas que usan APIs RESTful, la seguridad es una prioridad. Uno de los métodos más efectivos y populares para manejar autenticación y autorización es mediante JSON Web Tokens (JWT). Este artículo es una guía completa para desarrolladores que buscan implementar JWT en sus aplicaciones de manera eficiente y segura.
¿Qué es un JWT y por qué usarlo?
Un JSON Web Token es un estándar abierto (RFC 7519) que se utiliza para transmitir información entre dos partes de forma segura como un objeto JSON. Un JWT tiene tres componentes principales:
- Header: Describe el tipo de token y el algoritmo usado para la firma (por ejemplo, HS256).
- Payload: Contiene los datos que queremos transmitir, como el ID de usuario o permisos.
- Signature: Asegura que el token no ha sido modificado desde que fue emitido.
A diferencia de otros métodos de autenticación, los JWT son:
- Sin estado: No es necesario guardar tokens en el servidor, lo que reduce la carga de almacenamiento.
- Seguro: Usan algoritmos de firma (como HMAC o RSA) para garantizar la integridad del token.
- Flexible: Compatible con sistemas distribuidos y microservicios.
¿Cuándo deberías usar JWT?
Los JWT son ideales para aplicaciones que requieren autenticación en APIs RESTful. Algunos casos de uso comunes incluyen:
- Autenticación de usuarios en aplicaciones web o móviles.
- Manejo de sesiones sin estado en arquitecturas de microservicios.
- Compartir datos entre servicios confiables.
Diferencias entre JWT y Sesiones
Mientras que las cookies y las sesiones son métodos tradicionales de autenticación, los JWT son más adecuados para aplicaciones modernas. En la siguiente tabla os mostramos sus diferencias.
Característica | JWT | Sesiones |
---|---|---|
Modelo de almacenamiento | Token se almacena en el cliente (localStorage, sessionStorage o cookies) | Sesiones almacenan un identificador en cookies, con datos en el servidor. |
Autenticación | Stateless (sin estado): el servidor no guarda información del usuario. | Stateful (con estado): el servidor guarda datos en memoria o base de datos. |
Escalabilidad | Ideal para aplicaciones distribuidas y microservicios, ya que no depende del estado del servidor. | Menos escalable, ya que requiere sincronización de estado en el servidor. |
Seguridad | Firma digital (HS256, RS256) asegura la integridad, pero puede ser comprometido si se expone la clave secreta. | Más seguro en servidores centralizados, ya que los datos sensibles no están en el cliente. |
Revocación | Difícil, requiere listas negras o acortar el tiempo de vida del token. | Fácil, basta con eliminar el estado del usuario en el servidor. |
Transporte | Puede enviarse como header (Authorization: Bearer) o almacenarse en cookies. | Utiliza cookies HTTP para transporte automático. |
*Ejemplo práctico*: Cómo implementar JWT en Node.js
A continuación, os dejamos un ejemplo de sistema muy básico de autenticación JWT en JavaScript, en concreto en un proyecto con Express.js. Si sigues esta guía, tendrás una API protegida con rutas autenticadas en pocos minutos.
Paso 1: Configuración del proyecto
Primero, necesitas configurar un proyecto Node.js básico. Este paso incluye inicializar el proyecto, instalar las dependencias necesarias y configurar el entorno. Como gestor de paquetes usaremos npm
e instalaremos las siguientes librerías.
mkdir jwt-auth && cd jwt-auth
npm init -y
npm install express jsonwebtoken body-parser dotenv
Crearemos un archivo .env
y ahí guardaremos la clave secreta que usaremos para firmar y desencriptar los tokens.
// .env
SECRET_KEY=mi_clave_super_segura
Paso 2: Crear el servidor con Express
El siguiente paso es configurar un servidor Express. Aquí definimos las rutas y conectamos los middlewares necesarios, como el análisis de datos JSON.
// server.js
require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
// Importa las rutas y middlewares
const authRoutes = require('./routes/auth');
const protectMiddleware = require('./middlewares/protected');
const app = express();
app.use(bodyParser.json());
// Rutas públicas
app.use('/auth', authRoutes);
// Ruta protegida como ejemplo
app.get('/protected-route', protectMiddleware, (req, res) => {
res.json({ message: `Bienvenido, usuario ${req.user.username}` });
});
app.listen(3000, () => console.log('Servidor corriendo en http://localhost:3000'));
Paso 3: Generar JWT en el proceso de login
El login es el punto de entrada para autenticar usuarios. Cuando un usuario se autentica correctamente, el servidor genera un token JWT que el cliente puede usar para acceder a recursos protegidos.
Para simplificar, no hacemos uso de una base de datos y usamos el objeto USER
. En un caso real, aquí tendríamos que acceder a nuestra base de datos.
// routes/auth.js
const express = require('express');
const jwt = require('jsonwebtoken');
const router = express.Router();
// Datos ficticios de usuario
const USER = { username: 'developer1', password: '12345', id: 1 };
router.post('/login', (req, res) => {
const { username, password } = req.body;
if (username !== USER.username || password !== USER.password) {
return res.status(401).json({ message: 'Credenciales inválidas' });
}
const token = jwt.sign(
{ userId: USER.id, username: USER.username },
process.env.SECRET_KEY,
{ expiresIn: '1h' }
);
res.json({ token });
});
module.exports = router;
Paso 4: Proteger rutas con middleware
Para asegurarnos de que solo los usuarios autenticados puedan acceder a ciertas rutas, creamos un middleware que verifica la validez del token enviado en los encabezados HTTP.
// middlewares/protected.js
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
const token = req.headers['authorization'];
if (!token) {
return res.status(403).json({ message: 'Token no proporcionado' });
}
try {
const decoded = jwt.verify(token.split(' ')[1], process.env.SECRET_KEY);
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ message: 'Token inválido o expirado' });
}
};
Este middleware es el que hemos aplicado en la ruta /protected-route
en el server.js
. Por lo tanto, antes de ejecutarse el código que hay en la ruta, primero debe validarse todo lo que está en el middleware.
Paso 5: Probar la autenticación
En primer lugar, tenemos que iniciar el servidor Express.
node server.js
A continuación, seguiremos el siguiente esquema cliente-servidor para probar la autenticación con JWT.
Con una herramienta como Postman o Thunder Client puedes probar el flujo de autenticación. Primero, solicita un token mediante un POST
con los datos de autenticación {"username": "developer1", "password": "12345"}
a la ruta de login /auth/login
.
El token que nos devuelve es el que nos va a permitir acceder a las rutas protegidas por el middleware. Accederemos con un GET
a /protected-route
usando el esquema de autenticación Bearer
.
Usando Thunder Client ya hay un espacio para ello, pero no deja de ser una cabecera HTTP del tipo:
Authorization: Bearer <TOKEN>
También, podemos probar el caso en el que el token fuera inválido.
Paso 6: Buenas prácticas con JWT
- Evita exponer claves secretas: Usa variables de entorno y nunca las incluyas en tu código fuente.
- Haz que los tokens sean de corta duración: Usa tiempos de expiración (
expiresIn
) razonables. - Implementa Refresh Tokens: Para tokens de larga duración, usa un sistema de refresh tokens que permita emitir nuevos JWT sin requerir un nuevo login.
- Usa HTTPS: Protege los datos en tránsito cifrando las conexiones.
Si quieres saber más acerca de los JSON Web Tokens aquí tienes su documentación oficial.
Post originalmente publicado en https://codewebnow.com/blog/jwt/.
Top comments (0)