Comprendiendo los Tokens JWT: Una Guía Completa
· 12 min de lectura
Tabla de Contenidos
- ¿Qué son los JSON Web Tokens (JWT)?
- Estructura y Anatomía Detallada de JWT
- Creando y Utilizando JWTs en la Práctica
- Flujo de Autenticación JWT Explicado
- Mejores Prácticas de Seguridad para la Implementación de JWT
- JWT vs. Autenticación Basada en Sesiones
- Ventajas de JWT en Aplicaciones del Mundo Real
- Errores Comunes de JWT y Cómo Evitarlos
- Herramientas y Bibliotecas para Mejorar el Manejo de JWT
- Pruebas y Depuración de Implementaciones JWT
- Preguntas Frecuentes
- Artículos Relacionados
¿Qué son los JSON Web Tokens (JWT)?
Los JSON Web Tokens (JWT) se han convertido en el estándar de facto para transmitir información de forma segura entre partes en aplicaciones web modernas. Definidos por RFC 7519, los JWTs proporcionan un método compacto y seguro para URL para representar reclamaciones que se transferirán entre dos partes.
A diferencia de la autenticación tradicional basada en sesiones donde el servidor mantiene el estado, los JWTs son autocontenidos. Esto significa que el token en sí mismo contiene toda la información necesaria para verificar la identidad y los permisos de un usuario. Esta naturaleza sin estado hace que los JWTs sean particularmente valiosos en sistemas distribuidos, arquitecturas de microservicios y aplicaciones móviles.
Los JWTs sirven dos propósitos principales en las aplicaciones modernas:
- Autorización: Una vez que un usuario inicia sesión, cada solicitud subsiguiente incluye el JWT, permitiendo al usuario acceder a rutas, servicios y recursos permitidos con ese token. Las implementaciones de Single Sign-On (SSO) dependen en gran medida de los JWTs debido a su pequeña sobrecarga y capacidades entre dominios.
- Intercambio de Información: Los JWTs proporcionan una forma segura de transmitir información entre partes. Debido a que los JWTs pueden ser firmados usando pares de claves públicas/privadas, puedes verificar que los remitentes son quienes dicen ser y que el contenido no ha sido alterado.
La belleza de los JWTs radica en su simplicidad y versatilidad. Funcionan sin problemas en diferentes lenguajes de programación y plataformas, haciéndolos ideales para entornos heterogéneos donde tu frontend podría estar en React, tu backend en Node.js, y tu aplicación móvil en Swift o Kotlin.
Consejo profesional: Aunque los JWTs son increíblemente útiles, no son una solución mágica para todos los escenarios de autenticación. Comprender cuándo usar JWTs versus sesiones tradicionales es crucial para construir aplicaciones seguras y escalables.
Estructura y Anatomía Detallada de JWT
Un JWT consiste en tres partes distintas separadas por puntos (.), formando una estructura que se ve así: xxxxx.yyyyy.zzzzz. Cada sección sirve un propósito específico y juntas crean un paquete de información a prueba de manipulaciones.
El Encabezado
El encabezado típicamente consiste en dos partes: el tipo de token (JWT) y el algoritmo de firma que se está utilizando, como HMAC SHA256 o RSA. Esta información le dice a la parte receptora cómo validar la firma del token.
{
"alg": "HS256",
"typ": "JWT"
}
El encabezado luego se codifica en Base64Url para formar la primera parte del JWT. Los algoritmos comunes que encontrarás incluyen:
- HS256 (HMAC con SHA-256): Algoritmo simétrico usando un secreto compartido
- RS256 (Firma RSA con SHA-256): Algoritmo asimétrico usando pares de claves públicas/privadas
- ES256 (ECDSA con SHA-256): Algoritmo asimétrico usando criptografía de curva elíptica
La Carga Útil
La carga útil contiene las reclamaciones, que son declaraciones sobre una entidad (típicamente el usuario) y metadatos adicionales. Las reclamaciones se categorizan en tres tipos:
Reclamaciones registradas: Estas son reclamaciones predefinidas que no son obligatorias pero recomendadas para proporcionar interoperabilidad. Incluyen:
iss(emisor): Identifica quién emitió el tokensub(sujeto): Identifica el sujeto del token (usualmente el ID del usuario)aud(audiencia): Identifica los destinatarios para los que está destinado el JWTexp(tiempo de expiración): Marca de tiempo después de la cual el JWT expiranbf(no antes de): Marca de tiempo antes de la cual el JWT no debe ser aceptadoiat(emitido en): Marca de tiempo cuando el JWT fue emitidojti(ID de JWT): Identificador único para el JWT
Reclamaciones públicas: Estas son reclamaciones personalizadas que defines para tu aplicación. Para evitar colisiones, deben definirse en el Registro IANA de JSON Web Token o usar nombres resistentes a colisiones (como URIs con espacios de nombres).
Reclamaciones privadas: Reclamaciones personalizadas creadas para compartir información entre partes que acuerdan usarlas. Estas no son reclamaciones registradas ni públicas.
{
"sub": "1234567890",
"name": "John Doe",
"email": "[email protected]",
"role": "admin",
"iat": 1516239022,
"exp": 1516242622
}
La carga útil luego se codifica en Base64Url para formar la segunda parte del JWT.
Consejo rápido: Nunca almacenes información sensible como contraseñas o números de tarjetas de crédito en las cargas útiles de JWT. Aunque los JWTs están firmados, no están encriptados por defecto, lo que significa que cualquiera puede decodificar y leer la carga útil.
La Firma
La firma se crea tomando el encabezado codificado, la carga útil codificada, un secreto (para algoritmos HMAC) o clave privada (para RSA/ECDSA), y firmándolos usando el algoritmo especificado en el encabezado.
Por ejemplo, si se usa HMAC SHA256, la firma se crearía así:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
La firma asegura que el token no ha sido alterado. Si alguien cambia incluso un solo carácter en el encabezado o la carga útil, la verificación de la firma fallará.
Ejemplo Completo de JWT
Cuando juntas las tres partes, obtienes un JWT completo que se ve así:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Puedes decodificar e inspeccionar cualquier JWT usando nuestra Herramienta de Decodificación JWT para ver su encabezado, carga útil y verificar su firma.
Creando y Utilizando JWTs en la Práctica
Crear JWTs es sencillo con las bibliotecas adecuadas. Exploremos cómo generar y usar JWTs en diferentes lenguajes de programación y escenarios.
Creando JWTs en Node.js
La biblioteca jsonwebtoken es la opción más popular para aplicaciones Node.js:
const jwt = require('jsonwebtoken');
// Crear un token
const payload = {
sub: user.id,
email: user.email,
role: user.role
};
const secret = process.env.JWT_SECRET;
const options = {
expiresIn: '1h',
issuer: 'myapp.com'
};
const token = jwt.sign(payload, secret, options);
// Verificar un token
try {
const decoded = jwt.verify(token, secret);
console.log(decoded);
} catch (error) {
console.error('Token inválido:', error.message);
}
Creando JWTs en Python
Los desarrolladores de Python típicamente usan la biblioteca PyJWT:
import jwt
import datetime
# Crear un token
payload = {
'sub': user.id,
'email': user.email,
'role': user.role,
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1),
'iat': datetime.datetime.utcnow()
}
secret = os.environ.get('JWT_SECRET')
token = jwt.encode(payload, secret, algorithm='HS256')
# Verificar un token
try:
decoded = jwt.decode(token, secret, algorithms=['HS256'])
print(decoded)
except jwt.ExpiredSignatureError:
print('El token ha expirado')
except jwt.InvalidTokenError:
print('Token inválido')
Creando JWTs en Java
Para aplicaciones Java, la biblioteca java-jwt de Auth0 es ampliamente utilizada:
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
// Crear un token
Algorithm algorithm = Algorithm.HMAC256(secret);
String token = JWT.create()
.withSubject(user.getId())
.withClaim("email", user.getEmail())
.withClaim("role", user.getRole())
.withExpiresAt(new Date(System.currentTimeMillis() + 3600000))
.withIssuer("myapp.com")
.sign(algorithm);
// Verificar un token
try {
DecodedJWT jwt = JWT.require(algorithm)
.withIssuer("myapp.com")
.build()
.verify(token);
} catch (Exception e) {
System.out.println("Token inválido: " + e.getMessage());
}
Mejores Prácticas para la Generación de Tokens
Al crear JWTs, sigue estas pautas para asegurar seguridad y confiabilidad:
- Usa secretos fuertes: Tu secreto JWT debe tener al menos 256 bits (32 caracteres) de datos aleatorios. Nunca codifiques secretos directamente en tu código fuente.
- Establece tiempos de expiración apropiados: Los tokens de acceso deben ser de corta duración (15 minutos a 1 hora), mientras que los tokens de actualización pueden durar más (días a semanas).
- Incluye solo las reclamaciones necesarias: Mantén tu carga útil ligera para reducir el tamaño del token y minimizar la exposición de información.
- Usa el algoritmo correcto: Para la mayoría de las aplicaciones, HS256 es suficiente. Usa RS256 cuando necesites verificar tokens sin compartir secretos (como en microservicios).
Consejo profesional: Considera implementar un mecanismo de actualización de tokens usando tokens de actualización. Esto te permite mantener los tokens de acceso de corta duración por seguridad mientras mantienes una buena experiencia de usuario sin reautenticación frecuente.
Flujo de Autenticación JWT Explicado
Comprender cómo fluyen los JWTs a través de tu aplicación es crucial para implementarlos correctamente. Recorramos un flujo de autenticación típico paso a paso.
Autenticación Inicial
- El usuario envía credenciales: El usuario envía su nombre de usuario y contraseña al punto final de autenticación (típicamente
POST /api/auth/login). - El servidor valida las credenciales: El servidor verifica las credenciales contra la base de datos, verificando que el hash de la contraseña coincida.
- El servidor genera JWT: Tras una validación exitosa, el servidor crea un JWT que contiene la identidad del usuario y las reclamaciones relevantes.
- El servidor devuelve el token: El JWT se envía de vuelta al cliente, típicamente en el cuerpo de la respuesta. Algunas implementaciones también lo establecen como una cookie HTTP-only.
Solicitudes Subsiguientes
- El cliente incluye el token: Para cada solicitud subsiguiente, el cliente incluye el JWT en el encabezado de Autorización:
Authorization: Bearer <token> - El servidor valida el token: El servidor extrae el token, verifica su firma y comprueba su expiración.
- El servidor procesa la solicitud: Si el token es válido, el servidor extrae la información del usuario de la carga útil y procesa la solicitud en consecuencia.
- El servidor devuelve la respuesta: Los datos solicitados o el resultado de la operación se devuelven al cliente.
Flujo de Actualización de Token
Cuando el token de acceso expira, el flujo de actualización entra en acción:
- El cliente detecta la expiración: El cliente recibe una respuesta 401 No Autorizado o verifica la reclamación
expdel token. - El cliente envía el token de actualización: El cliente envía el token de actualización a un punto final de actualización dedicado (
POST /api/auth/refresh). - El servidor valida el token de actualización: El servidor verifica el token de actualización y comprueba si ha sido revocado.
- El servidor emite un nuevo token de acceso: Se genera un nuevo token de acceso y se devuelve al cliente.
- El cliente reintenta la solicitud original: El cliente usa el nuevo token de acceso para reintentar la solicitud fallida.
Flujo de Cierre de Sesión
Cerrar sesión con JWTs requiere consideración especial ya que los tokens no tienen estado:
- El cliente inicia el cierre de sesión: El usuario hace clic en cerrar sesión, activando una solicitud a
POST /api/auth/logout. - El servidor invalida el token de actualización: El servidor agrega el token de actualización a una lista de revocación o lo elimina de la base de datos.
- El cliente descarta los tokens: El cliente elimina tanto los tokens de acceso como de actualización del almacenamiento.
- El cliente redirige: El usuario es redirigido a la página de inicio de sesión o área pública.
| Tipo de Token | Vida Útil Típica | Ubicación de Almacenamiento | Propósito |
|---|---|---|---|
| Token de Acceso | 15 min - 1 hora | Memoria o sessionStorage | Autorizar solicitudes API |
| Token de Actualización | 7 días - 30 días | Cookie HTTP-only o almacenamiento seguro | Obtener nuevos tokens de acceso |
| Token de ID | Igual que el token de acceso | Memoria | Información de identidad del usuario (OpenID Connect) |
Consejo rápido: Implementa la actualización automática de tokens en tu aplicación cliente para manejar la expiración de tokens sin problemas. Esto evita que los usuarios sean desconectados inesperadamente