Qué es un JWT y para qué se usa hoy
Un JSON Web Token (JWT) es un token compacto y auto-contenido que transmite claims (afirmaciones) entre partes de forma segura. En cristiano: es una “tarjeta firmada” que el servidor emite y el cliente presenta para demostrar quién es y qué puede hacer. Es ubicuo en APIs REST, microservicios, SPAs (React, Vue, Angular) y móvil con OAuth 2.0/OpenID Connect.
A mí me gustan por su portabilidad y porque evitan consultas al servidor en cada request para resolver la sesión. Pero hay que usarlos con cabeza: los JWT son credenciales y, si se filtran, un atacante puede usarlos hasta que expiren. En mi caso, empecé usando HS256 “por simplicidad” y aprendí que es mejor dar el salto a RS256/ES256 cuando hay varios consumidores y necesitas rotación de claves sin tocar a todos los clientes.
Beneficios rápidos
Stateless en los recursos: menos carga de sesión.
Interoperabilidad: JSON sobre HTTP, fácil en cualquier stack.
Extensible: puedes modelar claims según tu negocio.
Riesgos si se implementa mal
Guardar tokens en lugares inseguros (me pasó con
localStorageen un staging con XSS).Expiraciones largas que abren ventanas de abuso.
Firmas débiles o claves sin rotación.
Estructura de un JWT: header, payload y signature
Un JWT son tres partes en Base64URL separadas por puntos:
Header: tipo y algoritmo. Ejemplo:
alg: HS256, RS256, ES256…typ: “JWT”.kid: identificador de clave para key rotation (imprescindible si publicas JWKS).
Payload (claims): datos firmados (no cifrados por defecto).
Signature: asegura integridad y autenticidad. Si alguien altera el payload, la firma no valida.
Nota: si necesitas confidencialidad, usa JWE (JWT cifrado) o no metas información sensible en el payload.
Claims esenciales y recomendados (con ejemplos)
| Claim | Para qué sirve | Recomendación |
|---|---|---|
iss | Quién emitió el token | URL estable del emisor |
sub | Identidad del sujeto | ID global e inmutable |
aud | Quién debe aceptar el token | Un valor por API/cliente |
exp | Cuándo expira | Tokens cortos (5–15 min) |
iat | Cuándo se emitió | Útil para diagnósticos |
nbf | No válido antes de… | Margen por clock skew |
jti | ID único del token | Útil para revocación selectiva |
scp/scope | Permisos | Manténlo breve; autorización fina fuera del token |
En mi caso, cuando el token “engordó” con permisos detallados, moví la autorización granular a un PDP externo (FGA/ABAC) y dejé en el JWT solo sub, iss, aud, exp y un scp breve.
Cómo funciona la autenticación con JWT (paso a paso)
Login (usuario + factor opcional) → el servidor emite access token (corto) y refresh token (más largo).
El cliente llama a la API con
Authorization: Bearer <access_jwt>.La API verifica firma y valida claims (aud, iss, exp, nbf).
Si el access expira, el cliente usa el refresh para obtener uno nuevo.
Rotación: cada uso del refresh invalida el anterior.
Yo probé a almacenar access + refresh en localStorage y me llevé un susto por un XSS en un entorno de pruebas. Desde entonces uso cookies HttpOnly + SameSite=Lax para el refresh, y envío el access token en el header (memoria/estado del cliente, no en storage persistente del navegador).
Validar vs verificar: diferencias que importan en producción
Verificar = comprobar firma con la clave adecuada (pública en RS/ES, secreta en HS).
Validar = además de la firma, chequear claims:
exp,nbf,aud,issy políticas propias (permisos, tenant, etc.).
En mi experiencia, los bugs más sutiles salieron por clock skew entre servicios: mitigado con NTP y un margen (nbf y tolerancia al validar exp ±30s).
Buenas prácticas: expiración corta, almacenamiento seguro y rotación de claves
Expiración corta
Access tokens entre 5 y 15 minutos; refresh días (según riesgo). Menos tiempo = menor ventana en caso de filtrado.
Almacenamiento
Web: refresh en cookie HttpOnly +
SameSite=Lax/Strict; access en memoria.Móvil: llévalo al Keychain/Keystore. Yo, en iOS, lo guardo en el Secure Enclave y uso DPoP para atar el token al dispositivo en endpoints críticos.
Rotación de claves (JWKS + kid)
Publica tu JWKS (/.well-known/jwks.json) y firma con claves rotativas identificadas por kid. Así clientes verifican automáticamente sin redeploy.
Revocación/rotación de refresh
Yo mantengo allowlist en Redis con jti y rotación: cada canje de refresh emite uno nuevo e invalida el anterior. Esto corta el replay.
JWT vs sesiones clásicas y vs SAML: cuándo conviene cada uno
JWT: APIs modernas, microservicios, multi-cliente. Pros: escalabilidad, desacoplo. Contras: revocación más compleja; cuidado con el tamaño de headers.
Sesiones con cookie: apps clásicas servidor-renderizadas. Pros: revocación inmediata; Contras: acoplamiento y sticky sessions.
SAML: entornos enterprise/SSO legado. Pros: maduro, XML-centric; Contras: más pesado; integración más compleja en SPAs.
Yo uso JWT para APIs y guardo cookie-sesión solo en paneles administrativos server-renderizados donde la revocación instantánea es crucial.
Errores comunes y cómo evitarlos (con checklist)
Checklist rápido
☐ No metas secretos/datos sensibles en el payload (no está cifrado).
☐ No uses
localStoragepara refresh; prefiere HttpOnly cookies.☐ Evita HS256 con secreto débil; considera RS256/ES256 y JWKS.
☐ Define y valida
iss,aud,exp,nbf.☐ Access corto; refresh rotado y con
jti.☐ Maneja clock skew.
☐ Limita el tamaño del token (evita permisos kilométricos).
☐ Registra
kidy rota claves periódicamente.
En mi equipo, el mayor salto de calidad vino de métricas: loguear fallos de verificación, aud inválidos y expiraciones para detectar clientes desincronizados.
Implementación rápida en Node.js y Spring (snippets)
Node.js (Express + RS256 + JWKS)
Spring Boot (Spring Security 6 + RS256 + JWKS)
En ambos stacks, yo añado rotación de refresh con Redis y un endpoint /token/refresh que invalida el refresh usado y emite otro. Esto me salvó de un intento de replay en producción.
FAQ sobre JWT (preguntas reales de equipos de desarrollo)
¿JWT es autenticación o autorización?
Es un portador de credenciales. Suele probar identidad (autenticación) y transportar permisos (autorización), pero el control fino puede vivir fuera.
¿Dónde guardo el token en web?
Access en memoria; refresh en cookie HttpOnly con SameSite. Yo migré desde localStorage después de un XSS en staging.
¿Qué duración pongo?
Access 5–15 min; refresh días. Ajusta según riesgo y añade detección de anomalías (IPs/UA inusuales).
¿Cómo revoco tokens ya emitidos?
Incluye jti y usa allowlist/denylist en servidor (Redis). Con access muy cortos, la revocación urgente afecta sobre todo al refresh.
¿Cuándo RS256 vs HS256?
HS256 si controlas emisor y verificador y puedes gestionar secretos robustos; RS256/ES256 si hay múltiples consumidores, necesitas JWKS o rotación fluida.
Conclusión
JWT no es plug-and-play, pero bien implementado te da escalabilidad y seguridad sin fricción. Mi receta: access corto, refresh rotado en cookie HttpOnly, RS256/ES256 con JWKS/kid, validaciones estrictas (iss, aud, exp, nbf) y métricas de uso. Si además extraes la autorización fina fuera del token, ganarás control sin inflar tus headers.