Autenticación con JWT paso a paso

En los últimos años los JSON Web Tokens (JWT) se han convertido en el estándar de facto para gestionar la autenticación sin estado en aplicaciones web. En este artículo te guiaré paso a paso, desde la teoría básica hasta una implementación mínima en Node.js, con ejemplos claros y consejos de seguridad.
¿Qué es un JWT?
Un JWT es simplemente una cadena codificada en Base64URL que contiene tres partes: header, payload y signature. Estas partes están separadas por puntos (.) y permiten que el servidor verifique la integridad del token sin necesidad de almacenar sesión alguna.
Estructura de un token
Veamos cada bloque:
- Header: indica el algoritmo de firma (por ejemplo
HS256) y el tipo (JWT). - Payload: lleva los claims, datos que describen al usuario (id, email, roles…) y campos reservados como
exp(expiración) oiat(emisión). - Signature: es el resultado de firmar
header.payloadcon una clave secreta o una clave privada, garantizando que nadie pueda modificar el token.
Flujo típico de autenticación
1. El cliente envía credenciales (usuario/contraseña) al endpoint /login.
2. El servidor valida esas credenciales contra la base de datos.
3. Si son correctas, genera un JWT y lo devuelve al cliente.
4. El cliente almacena el token (habitualmente en localStorage o en una cookie httpOnly) y lo incluye en el encabezado Authorization: Bearer <token> de cada petición protegida.
5. El servidor, al recibir la petición, verifica la firma y la validez temporal del token antes de conceder acceso.
Implementación mínima en Node.js con Express
Primero instalamos las dependencias:
npm install express jsonwebtoken dotenvCrearemos un archivo .env con la clave secreta:
JWT_SECRET=mi_clave_ultra_secretaA continuación, el código de ejemplo:
require('dotenv').config();
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());
// Simulación de usuarios
const users = [{ id: 1, email: 'alice@example.com', password: '1234' }];
app.post('/login', (req, res) => {
const { email, password } = req.body;
const user = users.find(u => u.email === email && u.password === password);
if (!user) return res.status(401).json({ message: 'Credenciales inválidas' });
const token = jwt.sign({ id: user.id, email: user.email }, process.env.JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
});
// Middleware de protección
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user; // queda disponible en la siguiente función
next();
});
}
app.get('/profile', authenticateToken, (req, res) => {
res.json({ message: 'Acceso concedido', user: req.user });
});
app.listen(3000, () => console.log('Server running on http://localhost:3000'));
En este ejemplo:
- Se genera el token con una expiración de una hora.
- El middleware
authenticateTokenextrae el token del encabezado y lo verifica. - Si la verificación falla, devolvemos
401(no autenticado) o403(token inválido).
Renovación y revocación
Los JWT son sin estado, lo que significa que el servidor no guarda una lista de tokens activos. Por eso, la estrategia típica es:
- Emitir tokens de corta vida (15‑30 minutos).
- Proveer un refresh token de mayor duración, almacenado en una cookie httpOnly, que permita solicitar un nuevo JWT sin volver a pedir credenciales.
- Si necesitas revocar un token antes de que expire (por ejemplo, al cerrar sesión), guarda su
jti(ID único) en una lista negra (en Redis o base de datos) y verifica contra ella en cada petición.
Buenas prácticas de seguridad
Para que tu implementación sea robusta, sigue estas recomendaciones:
- Clave secreta fuerte: usa al menos 256 bits y nunca la incluyas en el repositorio.
- Algoritmo de firma:
HS256es suficiente en la mayoría de los casos, pero si quieres separar la generación y verificación, usaRS256con pares de claves públicas/privadas. - HTTPS obligatorio: evita que el token sea interceptado en tránsito.
- Almacena el token de forma segura: en aplicaciones SPA,
localStoragees práctico pero vulnerable a XSS; las cookies httpOnly y SameSite=strict reducen ese riesgo. - Valida siempre el
exp: la libreríajsonwebtokenlo hace automáticamente, pero si implementas tu propio verificador, no lo olvides.
Con estos pasos tienes una base sólida para integrar JWT en cualquier proyecto backend. Desde aquí puedes explorar funcionalidades avanzadas como scopes, roles en el payload o incluso firmar tokens con claves asimétricas para micro‑servicios distribuidos.
¡Anímate a probarlo en tu próximo proyecto y comparte tus dudas en los comentarios!