OAuth 2.0: autorización delegada, no autenticación
Cómo OAuth 2.0 permite que una app actúe en tu nombre sin tu contraseña: los cuatro roles, los access tokens y refresh tokens, los tipos de grant y cuándo usar cada uno (Authorization Code con PKCE, Client Credentials, Device Code, Refresh Token, y los obsoletos Implicit y de contraseña), los scopes y el consentimiento, y qué cambia OAuth 2.1. Y la idea clave: OAuth autoriza al cliente; no autentica al usuario.
Una pregunta distinta de la de SAML
Sara quiere imprimir unas fotos. La app de impresión que encontró le pide conectarse a su almacenamiento en la nube. Diez años atrás, la forma de hacerlo habría sido escribir su contraseña de la nube en la app de impresión, que habría tenido control total de su cuenta durante todo el tiempo que quisiera. Hoy la envían a una página de su propio proveedor de la nube, que le pregunta si quiere dejar que esta app lea sus fotos. Dice que sí, vuelve, y la app puede ver sus fotos. Nunca vio su contraseña, no puede borrar nada y el acceso vence solo.
Eso es la autorización delegada, y OAuth 2.0 (RFC 6749) es el marco que la hace posible. SAML respondía “¿quién es este usuario y puede entrar en esta aplicación?”, es decir, autenticación e inicio de sesión único. OAuth responde una pregunta distinta, que despegó con las APIs y las aplicaciones de terceros en los años 2010: ¿cómo puede una aplicación actuar en tu nombre frente a un servicio sin que le entregues tu contraseña?
Los cuatro roles
OAuth tiene cuatro partes, y ordenarlas es casi todo el trabajo.
El propietario del recurso (resource owner) es el usuario dueño de los datos, que concede el acceso: Sara. El cliente es la aplicación que quiere acceder en su nombre: la app de impresión. El servidor de autorización autentica al propietario del recurso, muestra la pantalla de consentimiento y emite los tokens: el servicio de identidad de su proveedor de la nube. Y el servidor de recursos es la API que guarda los datos protegidos y acepta access tokens: la API de fotos de su proveedor de la nube.
flowchart LR accTitle: Los cuatro roles de OAuth 2.0 y cómo se relacionan accDescr: El propietario del recurso, el usuario, aprueba el acceso en el servidor de autorización. El servidor de autorización autentica al usuario y emite un access token para la aplicación cliente. El cliente presenta ese token ante el servidor de recursos, que guarda la API protegida y devuelve los datos. El cliente nunca recibe la contraseña del usuario. RO[Propietario del recurso<br/>el usuario] -->|aprueba el acceso| AS[Servidor de autorización<br/>emite tokens] AS -->|access token| C[Cliente<br/>la app] C -->|presenta el token| RS[Servidor de recursos<br/>la API] RS -->|datos protegidos| C
Separar el servidor de autorización del servidor de recursos es la buena idea discreta que está en el centro del diseño. El servidor de autorización es el único que maneja las credenciales y las decisiones del usuario; el servidor de recursos solo comprueba tokens. Por eso un solo servidor de autorización puede proteger cientos de APIs, y por eso una API puede aceptar tokens sin saber nada de cómo inició sesión el usuario.
Access tokens y refresh tokens
OAuth funciona con tokens al portador (bearer tokens): quien tiene uno puede usarlo, como el dinero en efectivo. Hay dos tipos, con vidas muy distintas.
Un access token es lo que el cliente presenta ante el servidor de recursos en cada llamada a la API. Tiene scopes, así que solo concede permisos concretos; dura poco, de minutos a una hora; y se usa todo el tiempo, lo que lo hace el más expuesto de los dos. Si lo roban, el daño queda limitado por sus scopes y su vida corta.
Un refresh token dura más, se queda en privado con el cliente y nunca se envía al servidor de recursos. Cuando el access token vence, el cliente canjea el refresh token en el servidor de autorización por un access token nuevo, para que el usuario no tenga que aprobar otra vez cada pocos minutos. Se usa poco y se guarda con cuidado, porque robar uno es mucho más grave, y por eso OAuth 2.1 exige que los refresh tokens se roten o queden atados al cliente que los tiene.
Un servidor de recursos que recibe un access token válido sabe que a quien lo tiene se le concedieron esos scopes. No sabe de forma fiable quién es el usuario; el token puede incluso pertenecer a una máquina sin ningún usuario detrás. El token responde qué se puede hacer, nunca quién lo está haciendo.
Cómo comprueba un token el servidor de recursos
Cuando llega un access token, el servidor de recursos tiene que decidir si es auténtico y qué permite. Hay dos modelos habituales, y importa cuál estás usando.
Con los tokens autocontenidos, el access token es un JWT firmado que el servidor de recursos comprueba localmente: verifica la firma con la clave pública del servidor de autorización y después comprueba el emisor (iss), la audiencia (aud), el vencimiento (exp) y los scopes. Es rápido y no necesita ninguna llamada de red, pero la revocación es débil, porque el token sigue siendo válido hasta que vence.
Con los tokens de referencia, el access token es una cadena opaca que el servidor de recursos envía al endpoint de introspección (RFC 7662) del servidor de autorización, que responde si está activo y qué scopes lleva. Cuesta una llamada de red por cada comprobación, pero la revocación es inmediata.
En los dos modelos, la comprobación de la audiencia no se negocia. El servidor de recursos tiene que confirmar que el token se emitió para él y no para otra API. Si se la salta, un token filtrado de un servicio de poco valor puede reutilizarse contra uno de mucho valor, un fallo muy común en la vida real. El artículo de tokens y formatos desarma los JWT en detalle.
Tipos de grant: qué flujo para qué cliente
Un tipo de grant, o flujo, es el procedimiento que usa un cliente para obtener un token. Elegir el adecuado para el tipo de cliente que estás construyendo es la decisión de diseño central de OAuth.
Authorization Code con PKCE
Es el flujo para aplicaciones web, aplicaciones de una sola página y apps móviles, en cualquier lugar donde haya una persona. El usuario se autentica en el servidor de autorización, el cliente recibe un código de autorización de vida corta mediante una redirección y después canjea ese código por tokens en una petición aparte, protegida con PKCE. Es lo que pasó cuando Sara conectó la app de impresión.
sequenceDiagram accTitle: Flujo Authorization Code de OAuth 2.0 con PKCE accDescr: El cliente crea un code verifier de PKCE y su challenge, que es el hash. Envía al usuario al servidor de autorización con el code challenge. El servidor de autorización autentica al usuario y muestra una pantalla de consentimiento, y después redirige de vuelta al cliente con un código de autorización de un solo uso. El cliente canjea el código junto con el verifier original en el endpoint de tokens, y el servidor de autorización comprueba el verifier contra el challenge anterior y devuelve un access token y un refresh token. actor U as Usuario participant C as Cliente (app) participant AS as Servidor de autorización participant RS as Servidor de recursos C->>C: Crea verifier y challenge de PKCE C->>AS: Solicitud de autorización (+ code_challenge) AS->>U: Autenticación y consentimiento AS->>C: Código de autorización (un solo uso) C->>AS: Canjea código + code_verifier AS->>AS: Comprueba que el challenge coincide AS->>C: Access token (+ refresh token) C->>RS: Llamada a la API con el access token RS->>C: Datos protegidos
PKCE, Proof Key for Code Exchange (RFC 7636), es lo que hace sólido el flujo. El cliente inventa un secreto aleatorio, el verifier, lo convierte en un hash, el challenge, y envía solo el challenge al empezar el flujo. Cuando canjea el código de autorización, tiene que presentar el verifier original, y el servidor de autorización comprueba que su hash coincide con el challenge que vio antes. Así, aunque un atacante intercepte el código de autorización, en un teléfono, en el historial del navegador, en un log, no puede canjearlo por tokens sin un secreto que nunca vio. PKCE empezó como una solución para apps móviles; hoy se recomienda en todo flujo Authorization Code, y OAuth 2.1 lo exige.
Client Credentials
Cuando no hay usuario, un servicio de back end que llama a la API de otro servicio, un proceso programado, un microservicio, no hay nadie a quien redirigir ni a quien pedirle consentimiento. El cliente se autentica como sí mismo, con sus propias credenciales, y recibe un access token limitado a lo que ese servicio puede hacer. Es el flujo correcto para la integración entre máquinas, y el único caso sin propietario del recurso: el cliente, en la práctica, actúa en su propio nombre.
sequenceDiagram accTitle: Flujo Client Credentials de OAuth 2.0 accDescr: Un servicio de back end se autentica directamente ante el servidor de autorización con su propio client ID y client secret, y puede pedir scopes. El servidor de autorización devuelve un access token limitado a lo que puede hacer el servicio. El servicio llama a la API del servidor de recursos con ese token y recibe los datos protegidos. En ningún momento interviene un usuario. participant SVC as Servicio que llama (cliente) participant AS as Servidor de autorización participant RS as Servidor de recursos (API) SVC->>AS: client_id + client_secret (+ scope) AS->>SVC: Access token SVC->>RS: Llamada a la API con el access token RS->>SVC: Datos protegidos
Device Code
Televisores inteligentes, dispositivos de streaming, herramientas de línea de comandos, dispositivos IoT: escribir una contraseña en ellos es incómodo o imposible. El Device Authorization Grant (RFC 8628) lo resuelve con elegancia. El dispositivo muestra un código corto y una URL, el usuario abre la URL en el teléfono o en el portátil, escribe el código y aprueba ahí, mientras el dispositivo sigue preguntándole al servidor de autorización hasta que llega la aprobación. El dispositivo nunca maneja credenciales; el usuario se autentica en algo con teclado.
sequenceDiagram
accTitle: Device Authorization Grant de OAuth 2.0
accDescr: Un dispositivo sin teclado le pide al servidor de autorización un código de dispositivo y un código de usuario, y recibe un código de dispositivo, un código de usuario y una URL de verificación. El dispositivo muestra el código de usuario y la URL. El usuario abre la URL en un teléfono o un portátil, inicia sesión y aprueba. Mientras tanto, el dispositivo consulta el endpoint de tokens hasta que el servidor de autorización informa la aprobación y devuelve un access token.
participant DEV as Dispositivo (TV / IoT)
participant AS as Servidor de autorización
actor U as Usuario (teléfono / portátil)
DEV->>AS: Pide código de dispositivo y de usuario
AS->>DEV: device_code, user_code, verification_uri
DEV->>U: Muestra "entra en la URL, escribe el CÓDIGO"
U->>AS: Abre la URL, inicia sesión, aprueba
loop Hasta que se apruebe
DEV->>AS: Consulta el endpoint de tokens (device_code)
end
AS->>DEV: Access tokenRefresh Token
No es un flujo de inicio de sesión sino un mecanismo de renovación: el cliente canjea su refresh token por un access token nuevo cuando vence el anterior, y mantiene el acceso sin molestar al usuario. Como los refresh tokens son poderosos y duran mucho, la práctica actual es la rotación: cada uso emite un refresh token nuevo e invalida el anterior, así que un token robado que se reutiliza aparece como una anomalía.
Implicit y el grant de contraseña: no los uses
Dos grants de los primeros tiempos hoy se desaconsejan activamente, y OAuth 2.1 los elimina. El grant Implicit devolvía el access token directamente en la redirección del navegador, en el fragmento de la URL, sin ningún canje de código, y filtraba tokens al historial del navegador, a los logs y a las cabeceras de referencia; Authorization Code con PKCE lo reemplaza, incluso en las aplicaciones de una sola página. El grant Resource Owner Password Credentials hacía que el cliente recogiera el usuario y la contraseña reales del usuario y los enviara al servidor de autorización, lo que anula todo el sentido de OAuth, porque el cliente ve la contraseña, y rompe el MFA y la federación. Ninguno de los dos tiene lugar en nada nuevo.
| Tipo de grant | Úsalo para | Estado |
|---|---|---|
| Authorization Code + PKCE | Aplicaciones web, de una sola página y móviles, donde hay un usuario | Recomendado |
| Client Credentials | Entre máquinas, sin usuario | Vigente |
| Device Code | Televisores, IoT, herramientas de línea de comandos, dispositivos sin teclado | Vigente |
| Refresh Token | Renovar el acceso sin volver a preguntar | Vigente |
| Implicit | Antes, aplicaciones de una sola página | Obsoleto; usa Code + PKCE |
| Contraseña (ROPC) | Antes, apps heredadas propias | Obsoleto; nunca para algo nuevo |
Una regla práctica cubre casi todas las decisiones reales. Una persona usando un navegador o una app significa Authorization Code con PKCE. Servicio a servicio sin nadie delante significa Client Credentials. Sin teclado significa Device Code. Mantener la sesión significa un refresh token que se rota. Y a cualquiera que proponga Implicit o el grant de contraseña para algo nuevo hay que frenarlo.
Scopes y consentimiento
Los scopes son la forma en que OAuth expresa el mínimo privilegio en el acceso delegado. Un scope es una cadena que nombra un permiso, photos.read, calendar.write, profile, y el cliente pide solo los scopes que necesita. El access token lleva esos scopes y el servidor de recursos los hace cumplir: un token con photos.read no puede escribir.
El consentimiento es el momento en que el propietario del recurso ve lo que se pide y lo aprueba, la pantalla de “esta app quiere leer tus fotos”. Es donde decide el usuario, no la plataforma. Dos hábitos definen un buen diseño de scopes y consentimiento. Pide poco: solicita el scope mínimo que hace el trabajo. Una app de impresión que pide acceso completo a la cuenta cuando solo necesita leer fotos es una señal de alarma, y usuarios y plataformas lo rechazan cada vez más. Y haz honesto el consentimiento: describe las capacidades reales en lenguaje claro. Los scopes amplios escondidos detrás de un consentimiento vago son la forma en que se engaña a los usuarios para que concedan de más, la versión delegada de la acumulación de privilegios.
Las implantaciones maduras suman dos refinamientos. Con la autorización incremental, una app pide lo mínimo para empezar y solicita más solo cuando una función lo necesita de verdad, así que la primera pantalla de consentimiento es chica y honesta y la app tiene scopes amplios solo mientras los usa. Con los indicadores de recurso (RFC 8707), un cliente dice para qué servidor de recursos es un token, y el servidor de autorización emite un token limitado a esa API en lugar de uno válido en todas partes. Eso reduce lo que puede hacer un token filtrado y refuerza la comprobación de audiencia del servidor de recursos.
OAuth 2.1: que el camino seguro sea el único
OAuth 2.1 no es un protocolo nuevo. Reúne diez años de recomendaciones de seguridad, buena parte de ellas del OAuth Security Best Current Practice, en una base más limpia. PKCE pasa a ser obligatorio en todo flujo Authorization Code, para clientes públicos y confidenciales por igual. Se eliminan los grants Implicit y de contraseña. Las URIs de redirección tienen que compararse por coincidencia exacta de cadena, sin comodines, lo que cierra un agujero de redirección abierta muy explotado. Y los refresh tokens deben estar atados a quien los envía o rotarse.
El hilo común es quitar en lugar de sumar. OAuth 2.0 ofrecía opciones inseguras que los desarrolladores seguían eligiendo, así que 2.1 las retira. Aprender los valores por defecto de OAuth 2.1 es aprender el consenso sobre cómo usar OAuth de forma segura.
Lo esencial de la seguridad
La flexibilidad de OAuth es también la forma en que la gente se lastima con él, y el OAuth 2.0 Security Best Current Practice recoge los peligros. Lo esencial cabe en un párrafo por idea.
Valida las URIs de redirección de forma exacta. Una coincidencia laxa permite que un atacante se envíe a sí mismo el código o el token, históricamente el fallo de OAuth más dañino. Usa siempre PKCE, que neutraliza los códigos de autorización interceptados. Usa el parámetro state, que ata la solicitud a la sesión del usuario y bloquea la falsificación de peticiones entre sitios en la redirección.
Trata los tokens como secretos. Los access tokens son credenciales al portador: envíalos solo por TLS, nunca los pongas en URLs ni en logs, y guárdalos con cuidado en el cliente. Rota los refresh tokens y mantén cortos los access tokens, para que un robo haga poco daño. Y limita cada token a una audiencia: emítelo para un servidor de recursos concreto y haz que cada servidor de recursos rechace los tokens que no vayan dirigidos a él, para que un token no pueda saltar de una API de poco valor a una de mucho valor.
La trampa que dio origen a OpenID Connect
Como OAuth le entregaba un token a una app después de que el usuario iniciara sesión en el proveedor, los desarrolladores supusieron que un access token válido significaba que el usuario estaba autenticado, y construyeron inicios de sesión encima. Pero un access token es un permiso al portador, no una prueba de quién es el usuario, ni siquiera de que haya un usuario presente. Un token emitido para una app podía reutilizarse en otra; un token podía pertenecer a una máquina. Usar OAuth como autenticación produjo errores reales de toma de control de cuentas.
La solución no fue abandonar OAuth sino añadirle encima una capa de identidad como corresponde: un token estándar que sí dice quién es el usuario, para qué app es y cuándo se autenticó. Esa capa es OpenID Connect, el próximo artículo. Mantén clara la distinción: OAuth autoriza al cliente; OIDC autentica al usuario. Las mismas redirecciones, la misma maquinaria de tokens, una declaración de identidad añadida, y un mundo de diferencia en lo que puedes concluir con seguridad.
Resumen
OAuth 2.0 es autorización delegada, no autenticación.
- Hay cuatro roles: el propietario del recurso (el usuario), el cliente (la app), el servidor de autorización (que emite los tokens) y el servidor de recursos (la API).
- Hay dos tokens: access tokens de vida corta y con scopes, usados en cada llamada, y refresh tokens más duraderos y bien guardados, que se canjean por access tokens nuevos.
- Los tipos de grant encajan con tipos de cliente: Authorization Code con PKCE para apps con usuario, Client Credentials entre máquinas, Device Code para dispositivos sin teclado, Refresh Token para renovar, y nunca Implicit ni el grant de contraseña.
- Los scopes y el consentimiento aportan mínimo privilegio: pide poco y describe el acceso con honestidad.
- OAuth 2.1 hace que el camino seguro sea el único: PKCE obligatorio, grants inseguros eliminados, coincidencia exacta de redirecciones.
- OAuth no es autenticación. Un access token es permiso, no identidad, que es justo por lo que existe OIDC.
Ejercicios prácticos
Están pensados para hacerlos, no solo para leerlos.
- Mira un flujo real. Abre la pestaña de red de tu navegador y usa un botón de “Iniciar sesión con Google” o “con GitHub” en cualquier sitio. Busca la petición
authorize, identifica los parámetrosclient_id,scope,redirect_uri,stateycode_challenge, y después busca dónde vuelve el código de autorización. - Lee el consentimiento. En la página de permisos de apps de un proveedor, como los ajustes de acceso de terceros de Google o GitHub, elige una app y enumera los scopes exactos que tiene. Decide cuáles son más amplios de lo que la app hace.
- Elige el grant. Indica el tipo de grant correcto para cada caso, con un motivo: una app de streaming para televisor, un proceso de facturación nocturno que llama a una API de pagos, una aplicación de una sola página en React y una app de banca móvil.
- Rómpelo en papel. Una aplicación de una sola página usa el grant Implicit y compara las URIs de redirección por prefijo. Describe dos ataques concretos que eso permite y los cambios de OAuth 2.1 que los cierran.
- Diseña un acceso entre máquinas. Dos de tus servicios de back end tienen que hablar entre sí. Indica el tipo de grant, cómo se autentica el servicio que llama, los scopes que definirías y cómo rotarías sus credenciales.
- Inspecciona un token. Consigue un access token en el entorno de pruebas para desarrolladores de un proveedor, como el OAuth Playground de Google. Si es un JWT, decodifícalo y busca
aud,scopeyexp; si es opaco, describe cómo lo comprobaría el servidor de recursos. Después enumera lo que el servidor de recursos debe verificar antes de confiar en él.
Tres preguntas para ponerte a prueba
- Un desarrollador dice: “Usamos OAuth para iniciar la sesión de los usuarios; si recibimos un access token válido, sabemos quiénes son”. Explica con exactitud por qué está mal y qué debería usar en su lugar.
- ¿Por qué un refresh token se trata como mucho más sensible que un access token, y qué dos prácticas limitan el daño si roban uno?
- Explica paso a paso cómo PKCE neutraliza un código de autorización interceptado, y por qué un código robado no le sirve por sí solo a un atacante.
Preguntas frecuentes
¿Qué es OAuth 2.0?
OAuth 2.0 (RFC 6749) es un marco de autorización delegada. Permite que una aplicación obtenga acceso limitado a los recursos de un usuario en otro servicio, como leer su calendario o publicar en su perfil, sin ver nunca su contraseña. El usuario aprueba un access token con scopes y con fecha de vencimiento, que la app presenta ante la API. Se trata de darle permiso a una app para actuar, no de iniciar la sesión de un usuario.
¿OAuth 2.0 autentica al usuario?
No, y es el punto más importante y más malentendido. OAuth autoriza a un cliente a acceder a recursos; no le dice a ese cliente nada fiable sobre quién es el usuario. Un access token es prueba de permiso, no de identidad. Usar un access token de OAuth tal cual como inicio de sesión es un antipatrón conocido, y autenticar al usuario es exactamente lo que añade OpenID Connect sobre OAuth.
¿Qué diferencia hay entre un access token y un refresh token?
Un access token es una credencial de vida corta que el cliente presenta ante el servidor de recursos para llamar a una API en nombre del usuario; lleva scopes y suele vencer en minutos. Un refresh token dura más, se queda en privado con el cliente y se canjea en el servidor de autorización por access tokens nuevos cuando vencen, para que el usuario no tenga que aprobar todo el tiempo. Los access tokens se usan mucho y están más expuestos; los refresh tokens se guardan con cuidado.
¿Qué tipo de grant de OAuth 2.0 debería usar?
Authorization Code con PKCE para casi todo lo que tiene un usuario: aplicaciones web, aplicaciones de una sola página y apps móviles. Client Credentials para llamadas entre máquinas sin usuario. Device Code para dispositivos sin teclado, como televisores e IoT. Refresh Token para renovar el acceso. Evita del todo los grants Implicit y de contraseña (Resource Owner Password Credentials); OAuth 2.1 los elimina.
¿Qué es PKCE y por qué es obligatorio?
PKCE (Proof Key for Code Exchange, RFC 7636) protege el flujo Authorization Code contra la interceptación. El cliente crea un secreto, el verifier, envía solo su hash al empezar y revela el verifier al canjear el código, así que un atacante que robe el código de autorización no puede canjearlo sin el secreto. Nació como solución para apps móviles y es obligatorio en todo flujo Authorization Code en OAuth 2.1.
¿Qué cambió en OAuth 2.1?
OAuth 2.1 reúne una década de buenas prácticas en una sola base: PKCE es obligatorio en todo flujo Authorization Code, se eliminan los grants Implicit y de contraseña, las URIs de redirección tienen que coincidir exactamente y los refresh tokens deben estar atados a quien los emite o rotarse. No inventa protocolos nuevos; quita las opciones inseguras que la gente seguía eligiendo.