OAuth2, OIDC y JWT sin misterio
Las tres siglas más confundidas de la autenticación, qué resuelve cada una, y los errores que aparecen en casi todo el código.
La confusión empieza en el nombre. OAuth2 lleva "auth" en el nombre y no va de login. OIDC sí va de login y casi nadie sabe su nombre completo. Y JWT no es ni una cosa ni la otra: es un formato de token que las dos usan.
Vamos por partes.
Autenticación no es autorización
Autenticación (authn) responde: ¿quién eres?
Autorización (authz) responde: ¿qué puedes hacer?
Son cosas distintas, resueltas por mecanismos distintos, y el error clásico es hacer bien la primera y olvidar la segunda en algún endpoint, que es exactamente el IDOR del artículo anterior.
OAuth2: autorización delegada
El problema que resuelve OAuth2: dar a una aplicación acceso limitado a un recurso tuyo sin entregarle tu contraseña.
Quieres que la app de agenda lea tu Google Calendar. Antes de OAuth, la solución era darle tu contraseña de Google a la app, lo que le daba acceso a todo, para siempre, sin forma de revocarlo.
Con OAuth2, te redirigen a Google, te autenticas allí, autorizas alcances específicos ("leer calendario"), y la app recibe un token de acceso limitado y revocable.
Los actores: el dueño del recurso (tú), el cliente (la app), el servidor de autorización (Google) y el servidor de recursos (la API de Calendar).
El flujo que debes usar hoy: Authorization Code con PKCE. Para todo tipo de cliente: web, móvil, SPA.
PKCE existe para resolver un ataque específico: en una app móvil, otra app podría interceptar el código de autorización en la redirección. Con PKCE, el cliente genera un secreto aleatorio, envía su hash al principio y el valor original al final. Quien interceptó el código no tiene el secreto.
Los flujos que ya no debes usar: implícito (devuelve el token directo en la URL, que se filtra en el historial y en los logs) y password grant (la app recibe la contraseña, lo que anula el propósito).
OIDC: login encima de OAuth2
OpenID Connect es una capa fina sobre OAuth2 que añade una cosa: el token de identidad.
Mientras el token de acceso dice "el portador de este token puede llamar a estas APIs", el token de identidad dice "esta persona es fulano, autenticada en tal momento, por tal método".
Por eso "Entrar con Google" es OIDC, no OAuth2 puro. Y por eso usar OAuth2 puro para login es un error conocido: el token de acceso no fue diseñado para probar identidad, y usarlo así abre la puerta a la confusión del diputado.
OIDC también estandariza el endpoint de descubrimiento
(/.well-known/openid-configuration), lo que hace que la integración sea casi automática en las
bibliotecas modernas.
JWT: el formato
Tres partes separadas por puntos: cabecera, datos (claims) y firma, cada una en base64url.
El malentendido fundamental: el JWT está firmado, no cifrado. Cualquiera lee el contenido. Pega uno en un decodificador y míralo.
Nunca pongas datos sensibles ahí dentro.
Lo que garantiza la firma es integridad: nadie alteró el contenido. Y autenticidad: lo emitió quien tiene la clave.
- OAuth2autorización delegadaDar a una app acceso limitado a tu recurso sin entregar la contraseña. No es login.
- OIDCloginCapa fina sobre OAuth2 que añade el token de identidad. Es lo que hay detrás de Entrar con Google.
- JWTformatoFirmado, no cifrado: cualquiera lo lee. Garantiza integridad y autenticidad, no secreto.
Los cuatro errores de JWT
1. Aceptar el algoritmo que viene en el token.
La cabecera dice qué algoritmo se usó. Si tu biblioteca confía en eso, un atacante manda
"alg": "none" y falsifica el token que quiera. O, peor, cambia RS256 por HS256 y usa la clave pública
(que es pública) como secreto de HMAC.
Corrección: fija el algoritmo esperado en el servidor. Nunca lo leas del token.
2. No validar aud e iss.
Un token perfectamente válido, emitido por un proveedor legítimo, para otro servicio, no puede valer en el tuyo. Si solo verificas la firma, vale.
3. Expiración demasiado larga, o ninguna.
Un token de acceso debe durar minutos. Renovar es papel del refresh token, que tiene almacenamiento y rotación distintos.
Un token de acceso con validez de treinta días es una credencial permanente circulando por logs, por cachés de proxy y por historiales de navegador.
4. Creer que se puede revocar.
No se puede. Un JWT es válido hasta que expira: esa es su naturaleza, y es la razón por la que escala sin consultar al emisor.
Si necesitas revocación inmediata (usuario despedido, cuenta comprometida), necesitas una lista de bloqueo consultada en cada petición. Y entonces tienes estado en el servidor otra vez, que era exactamente lo que el JWT prometía evitar.
La reflexión que se desprende: en muchos sistemas, una sesión tradicional en Redis es más simple, más segura e igual de rápida. El JWT resuelve un problema específico: validar sin consultar al emisor, en arquitectura distribuida o entre organizaciones. Si no tienes ese problema, quizá no lo necesites.
Dónde guardar el token
Dos opciones, con compromisos reales:
Cookie HttpOnly + Secure + SameSite: JavaScript no puede leerla, así que un XSS no roba el token. Vulnerable a CSRF, mitigado por SameSite. Es la recomendación estándar para aplicación web.
localStorage: cualquier XSS lo lee. No sufre CSRF. Es común en SPA y es una elección peor de lo que la mayoría asume.
Si guardas el token en localStorage, tu XSS de baja severidad se convirtió en compromiso de cuenta.
Modelos de autorización
Terminando con el "qué puedes hacer".
RBAC, basado en rol. Admin, editor, lector. Simple, y se vuelve un lío cuando aparecen los casos particulares: "editor, pero solo del departamento X", "lector, excepto datos financieros". La respuesta suele ser crear más roles, y en dos años tienes doscientos.
ABAC, basado en atributo. La regla evalúa atributos: departamento, importe de la transacción, hora, ubicación. Más expresivo, más difícil de auditar: responder "¿quién puede ver esto?" exige evaluar reglas.
ReBAC, basado en relación. "Puedes editar este documento porque eres dueño de la carpeta que lo contiene." Es el modelo de Zanzibar, de Google, y es el que resuelve el compartido jerárquico sin explotar en roles. Existen implementaciones abiertas (SpiceDB, OpenFGA, Ory Keto).
Para la mayoría de los sistemas, RBAC con algunos atributos resuelve. ReBAC vale cuando tu producto tiene el compartido y la jerarquía como funcionalidad central.
La checklist de cinco minutos
En tu código, hoy:
→ ¿El algoritmo del JWT está fijado en el servidor?
→ ¿Se validan aud e iss?
→ ¿Cuál es el TTL del token de acceso?
→ ¿Existe camino de revocación, y se ha probado?
→ ¿El token está en cookie HttpOnly o en localStorage?
→ ¿El flujo de OAuth es Authorization Code con PKCE?
→ ¿La autorización se verifica en el servidor en todos los endpoints, o solo en los que pasan por la pantalla?
Lee esto después
- IngenieríaPaso 20Legado: caracterizar, coser y estrangularTres técnicas que permiten tocar con seguridad un sistema que no escribiste, no entiendes, y no puedes parar.Leer artículo
- IngenieríaPaso 19Pruebas que valen lo que cuestanLa prueba no existe para demostrar que el código está bien. Existe para que puedas cambiarlo mañana sin miedo. Ese cambio de objetivo lo reorganiza todo.Leer artículo
- IngenieríaPaso 17Las vulnerabilidades que aparecen de verdadLa mayoría de las filtraciones que salen en las noticias no usa técnicas sofisticadas. Usa uno de estos seis fallos, y los seis tienen corrección conocida y barata.Leer artículo