OpenID Connect: la capa de identidad sobre OAuth

Cómo OIDC convierte OAuth 2.0 en autenticación real — el ID token (JWT) vs el access token, claims estándar (sub, iss, aud, exp, email…), los endpoints de discovery (.well-known/openid-configuration) y UserInfo, validación con JWKS, los flujos Authorization Code/Hybrid/Implicit, y el logout front-channel, back-channel y RP-initiated.

La capa que le faltaba a OAuth

El artículo anterior terminó con una advertencia: OAuth 2.0 autoriza a un cliente pero no autentica al usuario, y las apps que usaron un access token como login crearon agujeros de seguridad reales. OpenID Connect (OIDC) es el arreglo que la industria estandarizó — una capa de identidad delgada construida sobre OAuth 2.0, publicada por la OpenID Foundation en 2014, que agrega autenticación propia sin descartar nada de la maquinaria de OAuth.


El genio de OIDC es lo poco que agrega. Reutiliza sin cambios los flujos, endpoints y access tokens de OAuth, y atornilla justo las piezas que OAuth omitió a propósito: un token que afirma quién es el usuario (el ID token), un lugar para obtener más sobre él (el endpoint UserInfo), un vocabulario estándar para los atributos del usuario (claims), y una forma de que un cliente se configure automáticamente (discovery). Eso es todo. Cuando haces clic en “Iniciar sesión con Google”, estás usando OIDC, que usa OAuth por debajo.


Esa estandarización es todo el punto. Antes de OIDC, cada proveedor que ofrecía “social login” lo hacía con una extensión propietaria y ligeramente distinta de OAuth, y los desarrolladores escribían código de integración a medida para cada una. OIDC lo reemplazó con un único formato de ID token, un único documento de discovery, y un único procedimiento de validación que se comporta idéntico contra Google, Microsoft, Okta, Auth0 o cualquier proveedor compatible. Apréndelo una vez, intégralo en todas partes — justo por eso se difundió tan rápido.



ID token vs access token: la distinción central

El corazón de OIDC es un segundo token junto al access token de OAuth, con un propósito y una audiencia completamente distintos. Tener clara esta pareja es la mayor parte de entender OIDC.


flowchart TD
  accTitle: ID token versus access token en OpenID Connect
  accDescr: El authorization server emite dos tokens. El ID token es sobre identidad, está destinado al cliente, y es un JWT que el cliente lee y valida para autenticar al usuario. El access token es sobre autorización, está destinado al resource server, y el cliente debe tratarlo como opaco y solo reenviarlo a la API. El cliente usa el ID token para iniciar sesión al usuario y el access token para llamar a las APIs protegidas.
  AS[Authorization Server] --> IDT[ID token<br/>identidad — para el CLIENTE]
  AS --> AT[Access token<br/>autorización — para la API]
  IDT --> C[El cliente lo lee y valida<br/>para iniciar sesión al usuario]
  AT --> RS[El cliente lo reenvía<br/>para llamar al resource server]
Dos tokens, dos audiencias: el ID token es sobre identidad y es para que el cliente lo lea; el access token es sobre autorización y es para la API. Nunca uses uno para el trabajo del otro.

  • El ID token es sobre identidad y es para el cliente. Es un JWT firmado que el cliente abre, valida y lee para establecer quién acaba de iniciar sesión. Su audiencia (aud) es el cliente mismo.
  • El access token es sobre autorización y es para el resource server. El cliente debe tratarlo como opaco — no lo lee, solo lo reenvía a las APIs. Su audiencia es el resource server.

El error clásico es usar el access token para identificar al usuario (no está hecho para eso, y puede ni siquiera ser un JWT legible) o enviar el ID token a una API como si fuera un access token (la API no es su audiencia). La herramienta correcta para el trabajo correcto: ID token para saber quién, access token para llamar a qué.


El ID token: un JWT lleno de claims

El ID token es un JWT (JSON Web Token) — tres partes en base64url (header, payload, firma) que el cliente puede decodificar y verificar. Su payload es un conjunto de claims sobre el evento de autenticación y el usuario. Un payload decodificado se ve así:


{
  "iss": "https://accounts.example.com",
  "sub": "248289761001",
  "aud": "s6BhdRkqt3",
  "exp": 1718000300,
  "iat": 1718000000,
  "auth_time": 1717999990,
  "nonce": "n-0S6_WzA2Mj",
  "email": "sara@example.com",
  "email_verified": true,
  "given_name": "Sara",
  "family_name": "Lopez"
}

Los claims caen en dos grupos. Los claims de protocolo vuelven confiable al token:

  • iss — el emisor (qué proveedor lo acuñó). El cliente debe reconocerlo.
  • sub — el subject: un identificador estable y único del usuario en este emisor. Este, no el email, es la verdadera clave primaria — los emails cambian, sub no.
  • aud — la audiencia: el client ID para el que se acuñó este token. El cliente debe confirmar que es la audiencia destinataria.
  • exp / iat — timestamps de expiración y de emisión que acotan la validez del token.
  • auth_time / nonce — cuándo se autenticó realmente el usuario, y un valor que el cliente generó para atar el token a su solicitud y bloquear replay.

Los claims de identidad describen al usuario — email, email_verified, given_name, family_name, name, picture, etc. — poblados según los scopes solicitados. OIDC ata los claims a los scopes para que el cliente reciba solo lo que pide:


ScopeClaims que libera
openidsub (obligatorio — este scope es lo que vuelve OIDC a la solicitud)
profilename, given_name, family_name, picture, locale, …
emailemail, email_verified
addressaddress
phonephone_number, phone_number_verified

Solicita siempre openid (es obligatorio y es lo que dispara el ID token), luego agrega profile o email solo si de verdad los necesitas. La minimización de claims es el eco, a nivel de identidad, de la minimización de scopes de OAuth: no pidas atributos que no usarás.



Discovery, JWKS y UserInfo

OIDC estandariza cómo un cliente aprende sobre un proveedor y cómo obtiene las claves para validar tokens — que es lo que vuelve “agregar un nuevo proveedor de identidad” una tarea de configuración y no un proyecto de programación.


  • Discovery — todo proveedor compatible publica un documento JSON en /.well-known/openid-configuration que lista su authorization_endpoint, token_endpoint, userinfo_endpoint, la jwks_uri, y los scopes, claims y algoritmos que soporta. Un cliente apunta a la URL del emisor y se configura solo.
  • JWKS — la jwks_uri sirve las claves públicas de firma del proveedor (un JSON Web Key Set). El cliente las obtiene para verificar las firmas de los ID tokens, y como las claves se obtienen por kid (key ID), el proveedor puede rotar las claves de firma sin romper a nadie.
  • UserInfo — un endpoint protegido por OAuth que el cliente llama con el access token para recuperar claims adicionales sobre el usuario, cuando quieres datos de perfil más allá de lo que hay en el ID token.

sequenceDiagram
  accTitle: Discovery y validación del ID token en OIDC
  accDescr: El cliente obtiene el documento de discovery desde la URL well-known openid-configuration del proveedor y recibe las URLs de los endpoints y la URI de JWKS. El cliente luego obtiene el JSON Web Key Set desde la URI de JWKS para obtener las claves públicas de firma del proveedor. Cuando el cliente recibe después un ID token, selecciona la clave por su key ID y verifica la firma del token, el emisor, la audiencia y la expiración antes de confiar en sus claims.
  participant C as Cliente (RP)
  participant P as Proveedor OIDC
  C->>P: GET /.well-known/openid-configuration
  P->>C: Endpoints + jwks_uri
  C->>P: GET jwks_uri
  P->>C: Claves públicas de firma (JWKS)
  Note over C: Después: valida la firma del ID token por kid,<br/>luego revisa iss, aud, exp, nonce
El discovery vuelve la integración auto-configurable: el cliente lee el documento well-known del proveedor, obtiene las claves de firma de la URI de JWKS, y las usa para validar el ID token — y la rotación de claves simplemente funciona.

Los flujos

OIDC usa los grant types de OAuth, especializados para autenticación agregando el scope openid (que es lo que le dice al proveedor que además emita un ID token).


  • Authorization Code (con PKCE) — el flujo estándar y recomendado para esencialmente todo: apps web, SPAs y móviles. El cliente obtiene un code vía redirección y lo intercambia por un ID token y un access token. Este es el flujo OIDC que debes usar.
  • Hybrid — devuelve algunos tokens directamente desde el authorization endpoint y otros desde el token endpoint. Existe para escenarios específicos pero agrega complejidad y exposición de tokens en el front-channel; la mayoría de los equipos no lo necesitan.
  • Implicit — devolvía los tokens directamente en la redirección, como el grant implicit de OAuth. Deprecado por las mismas razones: los tokens se filtran al historial del navegador y a los logs. Authorization Code + PKCE lo reemplaza en todas partes, incluidas las SPAs.

El flujo Authorization Code en OIDC

El flujo es el Authorization Code + PKCE de OAuth, con una adición — el scope openid — que hace que el proveedor devuelva un ID token:


sequenceDiagram
  accTitle: Flujo OpenID Connect Authorization Code
  accDescr: El cliente redirige al usuario al authorization endpoint del proveedor OIDC solicitando el scope openid más un code challenge de PKCE y un nonce. El proveedor autentica al usuario y devuelve un authorization code de un solo uso. El cliente intercambia el code y el verifier de PKCE en el token endpoint y recibe tanto un ID token como un access token. El cliente valida el ID token para autenticar al usuario, luego opcionalmente llama al endpoint UserInfo con el access token para más claims.
  actor U as Usuario
  participant C as Cliente (RP)
  participant P as Proveedor OIDC
  C->>P: Authorize (scope=openid, code_challenge, nonce)
  P->>U: Autentica + consentimiento
  P->>C: Authorization code (un solo uso)
  C->>P: Intercambia code + code_verifier
  P->>C: ID token + access token
  C->>C: Valida ID token (firma, iss, aud, exp, nonce)
  C->>P: (opcional) UserInfo con access token
  P->>C: Claims adicionales
Flujo OIDC Authorization Code: el scope openid hace que el proveedor devuelva un ID token (identidad) junto al access token (autorización). El cliente valida el ID token para iniciar sesión al usuario.

La única diferencia conceptual con OAuth puro es el ID token que regresa y el cliente validándolo para autenticar. Todo lo demás — redirecciones, PKCE, el intercambio de code — es OAuth que ya conoces. Esa reutilización es justo por la que OIDC ganó: dio a los desarrolladores autenticación casi gratis sobre un framework de autorización que ya estaban desplegando.


Del ID token a una sesión

Una sutileza que confunde a los novatos: el ID token es prueba de que un login ocurrió, en un momento específico — no es un token de sesión de vida larga, y no está hecho para enviarse a tus propias APIs en cada solicitud. Su trabajo es ser validado una vez, justo después del login, para que el cliente pueda establecer quién es el usuario.


Lo que pasa después es responsabilidad del cliente. Tras validar el ID token, la app acuña su propia sesión — típicamente una cookie de sesión segura y http-only — y esa sesión, no el ID token, lleva al usuario a través de las solicitudes siguientes. El ID token cumplió su trabajo y puede descartarse.


Los dos tokens luego divergen limpiamente en su vida posterior:

  • ¿Necesitas seguir llamando a APIs como el usuario? Eso es autorización — usa el access token, y renuévalo con el refresh token de OAuth cuando expire. El ID token no juega ningún papel en las llamadas a la API.
  • ¿Necesitas mantener al usuario con sesión iniciada en tu app? Ese es el trabajo de tu sesión, regido por tu propia política de timeout, independiente de los tokens.

Un bug frecuente es enviar el ID token a un backend como credencial bearer, confundiendo “quién inició sesión” con “puede llamar a esta API”. Mantenlos separados: el ID token autentica el login, el access token autoriza las llamadas, y tu cookie de sesión mantiene la continuidad. Esta separación también explica por qué el logout es su propio problema — terminar tu sesión es fácil; terminar la sesión del proveedor y la de todas las demás apps es la parte difícil de abajo.


Logout: tres mecanismos, un problema difícil

Iniciar sesión es la mitad fácil; cerrarla limpiamente a través de muchas apps que comparten un proveedor es genuinamente difícil — la misma lección que enseñó el Single Logout de SAML. OIDC define tres mecanismos:


flowchart TD
  accTitle: Mecanismos de logout de OIDC
  accDescr: OIDC define tres mecanismos de logout. El logout RP-initiated hace que el relying party redirija al usuario al endpoint end-session del proveedor para terminar la sesión central. El logout front-channel usa el navegador para cargar URLs de logout ocultas en cada relying party. El logout back-channel hace que el proveedor haga llamadas directas servidor-a-servidor al endpoint de logout de cada relying party. Back-channel es el más fiable porque no depende del navegador.
  L[El usuario cierra sesión] --> RP[RP-initiated<br/>redirige al end_session del proveedor]
  RP --> FC[Front-channel<br/>el navegador carga URLs de logout ocultas]
  RP --> BC[Back-channel<br/>el proveedor llama a cada app servidor-a-servidor]
  FC --> R[Sesiones terminadas<br/>best-effort]
  BC --> R
Tres formas de logout: RP-initiated envía al usuario al proveedor; front-channel usa el navegador para pingear cada app; back-channel usa llamadas servidor-a-servidor. Back-channel es el más fiable, ninguno es a prueba de balas.

  • Logout RP-initiated: el relying party (app) redirige al usuario al end_session_endpoint del proveedor, terminando la sesión central en el proveedor. Es la base y el más soportado.
  • Logout front-channel: la página de logout del proveedor carga iframes ocultos apuntando a la URL de logout de cada app, para que el navegador dispare un logout local en todas partes. Depende de que el navegador permita esas solicitudes — las restricciones de cookies de terceros cada vez más lo socavan.
  • Logout back-channel: el proveedor hace llamadas directas servidor-a-servidor al endpoint de logout registrado de cada app, llevando un logout token. No depende del navegador, lo que lo vuelve el más fiable — y la recomendación moderna donde se soporta.

Como con SAML, la posición honesta es que el logout coordinado es best-effort: una sola app que esté caída o implemente mal su endpoint rompe la cadena. Las vidas de sesión cortas y la revocación en el proveedor siguen siendo los controles en los que de verdad confías. Federar el inicio de una sesión es fácil; federar su fin es un problema difícil recurrente en cada protocolo de este módulo.


Esenciales de seguridad

OIDC hereda los riesgos de OAuth y agrega algunos propios alrededor del ID token. Los innegociables, tomados de OIDC Core y de la guía de seguridad de OAuth:

  • Valida el ID token por completo. Verifica la firma, y rechaza alg: none de plano — históricamente un bug de JWT devastador donde un atacante quita la firma. Luego revisa iss, aud, exp, y el nonce que enviaste.
  • Usa state y PKCE. state bloquea CSRF en la redirección; PKCE protege el intercambio de code — ambos obligatorios en OIDC moderno igual que en OAuth 2.1.
  • Ata el nonce. Genera un nonce fresco por login, envíalo en la solicitud, y confírmalo en el ID token — esto es lo que evita que un ID token capturado sea reusado.
  • Protégete de los ataques de mix-up de IdP. Cuando un cliente confía en varios proveedores, confirma que el emisor que realmente respondió coincide con el que iniciaste, para que un emisor malicioso o confundido no pueda sustituir su token.
  • Nunca identifiques usuarios por el access token, y nunca envíes el ID token a una API como bearer — los errores de confusión de audiencia de la sección anterior son bugs de seguridad, no solo de estilo.
  • Minimiza los claims y prefiere el logout back-channel donde se soporte; mantén las sesiones cortas de todas formas.

El patrón rima con cada protocolo de este módulo: el estándar es sólido, y casi toda brecha real es una implementación que validó de menos. Para los despliegues de mayor aseguramiento (banca abierta, gobierno), los perfiles FAPI aprietan aún más estos requisitos.


OIDC y SAML: el mismo trabajo, eras distintas

OIDC y SAML ambos hacen autenticación federada, así que ¿cuándo usas cuál? El contraste de un vistazo:


DimensiónSAML 2.0OpenID Connect
Formato de datosXMLJSON / JWT
TransporteRedirección de navegador + POSTHTTP/REST + redirecciones
Artefacto de identidadSAML assertionID token (+ access token de OAuth)
Mejor paraSSO web empresarial y B2BWeb, móvil, SPA, consumidor
Origen2005, autónomo2014, construido sobre OAuth 2.0

SAML es XML, basado en redirecciones de navegador, y arraigado en SaaS empresarial y B2B. OIDC es JSON/JWT, amigable con REST, y el predeterminado para todo lo nuevo — login de consumidor, apps móviles, SPAs, y cada vez más la empresa también. Construcción nueva con opción: prefiere OIDC. Integrar con una app empresarial que solo habla SAML: usa SAML. La mayoría de las organizaciones grandes corren ambos, y la habilidad es la fluidez en cada uno en lugar de la lealtad a uno.



Recapitulación

OpenID Connect es autenticación construida limpiamente sobre OAuth:


  1. OIDC = OAuth 2.0 + una capa de identidad. Autentica al usuario; OAuth solo autorizaba al cliente.
  2. ID token vs access token: el ID token es identidad, para que el cliente lo lea; el access token es autorización, para la API. No intercambies sus trabajos.
  3. El ID token es un JWT de claimsiss, sub, aud, exp, iat, más claims de perfil/email — y debe validarse (firma, iss, aud, exp, nonce) antes de confiar en él.
  4. Discovery, JWKS y UserInfo vuelven la integración auto-configurable y la rotación de claves transparente.
  5. Authorization Code + PKCE es el flujo a usar; Hybrid es de nicho; Implicit está deprecado.
  6. El ID token autentica un evento de login, luego el cliente acuña su propia sesión y usa el access token para las APIs; el logout viene en formas RP-initiated, front-channel y back-channel — back-channel el más fiable, ninguno a prueba de balas, las sesiones cortas siguen siendo esenciales.


Ejercicios prácticos

Estos son prácticos — hazlos, no solo los leas:

  1. Lee el documento de discovery de un proveedor. Abre https://accounts.google.com/.well-known/openid-configuration (o el de cualquier proveedor) en tu navegador. Identifica el authorization_endpoint, token_endpoint, userinfo_endpoint y jwks_uri, y nota qué scopes y algoritmos de firma se soportan.
  2. Decodifica un ID token. Tras un flujo de “Iniciar sesión con…” (usa el playground OIDC de un proveedor), pega el ID token en un decodificador de JWT y etiqueta iss, sub, aud, exp y dos claims de identidad. Confirma que aud sea igual al client ID.
  3. Distingue los tokens (caso de uso). Dado un ID token y un access token del mismo login, decide cuál enviarías a una API backend y cuál usarías para renderizar “Bienvenida, Sara” — y explica por qué intercambiarlos es un bug.
  4. Audita una validación (caso de uso). Un cliente inicia sesión a los usuarios decodificando en base64 el payload del ID token y leyendo email, omitiendo las verificaciones de firma y aud. Lista cada verificación que le falta y el ataque que habilita cada omisión.
  5. Diseña el logout (caso de uso). Corres cinco apps detrás de un proveedor OIDC y necesitas “cerrar sesión en todas partes”. Elige entre logout front-channel y back-channel, justifícalo, y enuncia en qué seguirías confiando cuando una app esté caída al momento del logout.
  6. Mapea scopes a claims (caso de uso). Una función nueva necesita el nombre y el email verificado del usuario, nada más. Lista los scopes exactos que solicitarías, los claims que esperarías de vuelta, y dónde leerías cada uno (ID token vs UserInfo) — luego explica por qué pedir profile más email más un scope custom sería pedir de más.

Tres preguntas para autoevaluarte

  1. Explica, en una o dos frases cada uno, el propósito y la audiencia destinataria del ID token versus el access token, y da un bug concreto causado por confundirlos.
  2. ¿Por qué sub — no email — es el identificador estable correcto para un usuario, y qué se rompe si una app indexa sus cuentas por email?
  3. Un nuevo proveedor de identidad necesita agregarse a tu app con cero URLs hardcodeadas y rotación de claves transparente. ¿Qué funciones de OIDC lo hacen posible, y cómo trabajan juntas?

Preguntas frecuentes

¿Qué es OpenID Connect (OIDC)?

OpenID Connect es una capa de identidad delgada construida sobre OAuth 2.0 que agrega autenticación real del usuario. Donde OAuth solo autoriza a un cliente a acceder a recursos, OIDC además emite un ID token — un JWT firmado que afirma quién es el usuario, para quién es, y cuándo se autenticó. Es el estándar detrás de 'Iniciar sesión con Google/Microsoft/Apple', publicado por la OpenID Foundation en 2014.

¿Cuál es la diferencia entre un ID token y un access token?

Un ID token es sobre identidad y es para el cliente: un JWT firmado con claims sobre el usuario (sub, email, name) que el cliente valida para iniciarle sesión. Un access token es sobre autorización y es para el resource server: otorga acceso a la API y el cliente debe tratarlo como opaco. Usa el ID token para saber quién es el usuario; usa el access token para llamar a las APIs.

¿OpenID Connect reemplaza a OAuth 2.0?

No — está construido sobre OAuth 2.0, no es un reemplazo. OIDC reutiliza los flujos, endpoints y tokens de OAuth y agrega las piezas de identidad que OAuth dejó fuera a propósito: el ID token, el endpoint UserInfo, los claims estándar y el discovery. Cuando 'inicias sesión con' un proveedor estás usando OIDC, que usa OAuth por debajo. Son un solo stack, con OIDC manejando la autenticación y OAuth la autorización.

¿Qué es el endpoint de discovery .well-known/openid-configuration?

Es un documento JSON estándar que un proveedor OIDC publica en /.well-known/openid-configuration que lista todo lo que un cliente necesita para integrarse: las URLs de los endpoints de authorization, token y UserInfo, la URI de JWKS para las claves de verificación de firma, y los scopes, claims y algoritmos soportados. El discovery permite que un cliente se configure automáticamente en lugar de hardcodear URLs, y es cómo la rotación de claves se mantiene transparente.

¿Qué claims estándar contiene un ID token de OIDC?

Claims centrales que todo ID token lleva: iss (emisor), sub (el identificador único y estable del usuario), aud (el cliente para el que es), exp (expiración) e iat (issued-at), a menudo con auth_time y nonce. Los scopes profile y email agregan claims como email, email_verified, given_name, family_name y name. El cliente debe validar iss, aud, exp y la firma antes de confiar en cualquiera de ellos.

¿Cómo funciona el logout en OpenID Connect?

OIDC define tres mecanismos. El logout RP-initiated permite que la app envíe al usuario al endpoint end-session del proveedor para cerrar sesión de forma central. El logout front-channel usa el navegador para cargar URLs de logout ocultas en cada app. El logout back-channel hace que el proveedor llame al endpoint de logout de cada app servidor-a-servidor. Como en SAML, el single logout coordinado es genuinamente difícil y a menudo best-effort; las sesiones cortas siguen siendo el control fiable.