OAuth 2.0 y OpenID Connect Sin Atajos

Seguridad Intermediate 25 min Jan 12, 2026

Audiencia

Esta guía es para cualquier persona que necesite entender OAuth 2.0 y OpenID Connect conceptualmente:

  • Desarrolladores backend implementando autenticación y autorización en APIs
  • Desarrolladores frontend integrando botones de “Iniciar sesión con Google/GitHub/etc.”
  • Ingenieros de seguridad revisando implementaciones de OAuth en busca de vulnerabilidades
  • Arquitectos técnicos eligiendo entre estrategias de autenticación
  • Product managers entendiendo SSO e integraciones con terceros

Un entendimiento básico de HTTP y APIs es útil pero no requerido. Construiremos desde los principios fundamentales.

Objetivo

Después de leer esta guía, entenderás:

  • Qué problema resuelve OAuth 2.0 (y qué no resuelve)
  • Los cuatro roles en cada interacción OAuth
  • Cómo funcionan los flujos principales de OAuth (Authorization Code, Client Credentials)
  • Por qué existe PKCE y contra qué protege
  • La diferencia entre OAuth 2.0 y OpenID Connect
  • Cuándo usar ID tokens vs. access tokens
  • Cómo elegir el flujo correcto para tu aplicación

No estarás listo para implementar OAuth de forma segura (eso requiere un estudio más profundo), pero tendrás un modelo mental claro de cómo encaja todo.

1. El Problema que Resuelve OAuth

Imagina que estás construyendo un servicio de impresión de fotos. Los usuarios quieren imprimir fotos de su Google Drive. La solución ingenua: pedir a los usuarios su contraseña de Google.

Esto es terrible por múltiples razones:

  1. Confianza: Los usuarios deben confiar en ti con sus credenciales de Google
  2. Alcance del acceso: Obtienes acceso completo a su cuenta de Google, no solo a las fotos
  3. Revocación: Si los usuarios quieren dejar de usar tu servicio, deben cambiar su contraseña de Google
  4. Seguridad: Ahora almacenas contraseñas de cuentas que no son tuyas

OAuth resuelve esto habilitando la autorización delegada:

“Permite que los usuarios otorguen a tu aplicación acceso limitado a sus recursos en otro servicio, sin compartir sus credenciales.”

graph LR
    A[App de Impresión] -->|"¿Puedo acceder a
las fotos de Alice?"| B[Google] B -->|"Alice, ¿apruebas
esto?"| C[Alice] C -->|"Sí, solo
leer fotos"| B B -->|"Aquí tienes un token
solo para fotos"| A style A fill:#e3f2fd style B fill:#fff3e0 style C fill:#e8f5e9

Insight clave: OAuth trata sobre autorización (qué puedes hacer), no sobre autenticación (quién eres).

Lo que OAuth NO es

OAuth no te dice:

  • Quién es el usuario
  • El email o nombre del usuario
  • Si el usuario ha iniciado sesión en su cuenta

OAuth solo te dice: “Este token otorga acceso a estos recursos específicos.”

Esta distinción importa. Veremos cómo OpenID Connect añade identidad más adelante.

2. Los Cuatro Roles

Cada interacción OAuth involucra cuatro roles distintos. Entenderlos es esencial.

graph TB
    subgraph "Los Cuatro Roles de OAuth"
        RO[Resource Owner
El usuario dueño de los datos] C[Client
Tu aplicación] AS[Authorization Server
Emite tokens] RS[Resource Server
Aloja los datos protegidos] end RO -->|"Otorga permiso"| C C -->|"Solicita token"| AS AS -->|"Emite token"| C C -->|"Presenta token"| RS RS -->|"Devuelve datos"| C style RO fill:#e8f5e9 style C fill:#e3f2fd style AS fill:#fff3e0 style RS fill:#fce4ec

Resource Owner (Propietario del Recurso)

El Resource Owner es el usuario que posee los datos protegidos. En nuestro ejemplo de impresión de fotos, es la persona cuyas fotos de Google Drive quieres acceder.

El resource owner puede otorgar o denegar acceso a sus recursos. Es la autoridad final.

Client (Cliente)

El Client es tu aplicación—el software que quiere acceder a los datos del resource owner. Puede ser:

  • Una aplicación web
  • Una app móvil
  • Una aplicación de escritorio
  • Un servicio backend

Importante: En terminología OAuth, “client” significa tu aplicación, no el usuario final.

Authorization Server (Servidor de Autorización)

El Authorization Server autentica al resource owner y emite access tokens al client. Es el guardián.

Ejemplos:

  • Servidor OAuth de Google (accounts.google.com)
  • Servidor OAuth de GitHub (github.com/login/oauth)
  • El proveedor de identidad de tu empresa

El authorization server maneja:

  • Autenticación del usuario (login)
  • Pantallas de consentimiento ("¿Permitir a esta app…?")
  • Emisión de tokens
  • Validación de tokens (a veces)

Resource Server (Servidor de Recursos)

El Resource Server aloja los recursos protegidos y acepta access tokens. Es donde viven los datos.

Ejemplos:

  • API de Google Drive
  • API de GitHub
  • API interna de tu empresa

En muchos sistemas, el authorization server y el resource server son el mismo (o son operados por la misma organización). Pero conceptualmente, son roles separados.

Resumen de Roles

RolPregunta que RespondeEjemplo
Resource Owner¿De quién son los datos?Alice (la usuaria)
Client¿Qué app quiere acceso?Servicio de Impresión de Fotos
Authorization Server¿Quién emite tokens?Google OAuth
Resource Server¿Dónde están los datos?API de Google Drive

3. Flujo Authorization Code

El Flujo Authorization Code es el flujo OAuth más común y seguro. Se usa cuando tu aplicación tiene un servidor backend que puede almacenar secretos de forma segura.

Cuándo Usarlo

  • Aplicaciones web con un componente del lado del servidor
  • Apps móviles (con PKCE—más sobre esto después)
  • Cualquier escenario donde el client puede mantener un secreto

El Flujo Paso a Paso

sequenceDiagram
    participant U as Usuario (Navegador)
    participant C as Client (Tu App)
    participant AS as Authorization Server
    participant RS as Resource Server

    U->>C: 1. Click "Iniciar sesión con Google"
    C->>U: 2. Redirigir a Google
    U->>AS: 3. Usuario se autentica y da consentimiento
    AS->>U: 4. Redirigir de vuelta con código de auth
    U->>C: 5. Enviar código de auth al backend
    C->>AS: 6. Intercambiar código por tokens
    AS->>C: 7. Devolver access token (y refresh token)
    C->>RS: 8. Solicitar datos con access token
    RS->>C: 9. Devolver datos protegidos

Vamos paso a paso:

Pasos 1-2: Iniciar el flujo

El usuario hace click en “Iniciar sesión con Google” en tu app. Tu app lo redirige al endpoint de autorización de Google:

https://accounts.google.com/o/oauth2/v2/auth?
  response_type=code
  &client_id=TU_CLIENT_ID
  &redirect_uri=https://tuapp.com/callback
  &scope=https://www.googleapis.com/auth/drive.readonly
  &state=xyz123

Parámetros clave:

  • response_type=code: Queremos un código de autorización
  • client_id: El identificador de tu app (registrado con Google)
  • redirect_uri: A dónde enviar al usuario después de la autorización
  • scope: Qué permisos estás solicitando
  • state: Valor aleatorio para prevenir ataques CSRF

Paso 3: El usuario se autentica y da consentimiento

Google muestra una pantalla de login (si es necesario) y una pantalla de consentimiento:

“Servicio de Impresión de Fotos quiere:

  • Ver tus archivos de Google Drive

Permitir / Denegar”

Pasos 4-5: Se devuelve el código de autorización

Si el usuario aprueba, Google redirige de vuelta a tu redirect_uri:

https://tuapp.com/callback?
  code=4/0AX4XfWh...
  &state=xyz123

El code es el código de autorización—un token de corta duración y un solo uso.

Pasos 6-7: Intercambiar código por tokens

Tu servidor backend (¡no el navegador!) intercambia el código por tokens:

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=4/0AX4XfWh...
&redirect_uri=https://tuapp.com/callback
&client_id=TU_CLIENT_ID
&client_secret=TU_CLIENT_SECRET

Google responde con:

{
  "access_token": "ya29.a0AfH6SM...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "1//0g..."
}

Pasos 8-9: Acceder a recursos protegidos

Tu app usa el access token para llamar a la API de Google:

GET /drive/v3/files HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer ya29.a0AfH6SM...

¿Por Qué el Paso Extra?

¿Por qué no devolver el access token directamente? ¿Por qué el intercambio de código?

Seguridad. El código de autorización pasa por el navegador del usuario (en la URL). Si devolviéramos el access token ahí:

  • Sería visible en el historial del navegador
  • Podría ser registrado por proxies
  • Extensiones maliciosas del navegador podrían robarlo

Al requerir un intercambio en el backend con el client_secret, aseguramos:

  • Solo tu servidor puede obtener el access token
  • El token nunca toca el navegador
  • Incluso si alguien roba el código, no puede usarlo sin tu secreto

4. Flujo Client Credentials

El Flujo Client Credentials es más simple—es para comunicación máquina a máquina donde no hay un usuario involucrado.

Cuándo Usarlo

  • Servicios backend llamando a otros servicios backend
  • Trabajos programados accediendo a APIs
  • Cualquier escenario donde no hay un usuario humano

El Flujo

sequenceDiagram
    participant C as Client (Servicio Backend)
    participant AS as Authorization Server
    participant RS as Resource Server

    C->>AS: 1. Solicitar token con credenciales del client
    AS->>C: 2. Devolver access token
    C->>RS: 3. Solicitar datos con access token
    RS->>C: 4. Devolver datos

Pasos 1-2: Solicitar token

Tu servicio se autentica directamente con sus credenciales:

POST /token HTTP/1.1
Host: auth.ejemplo.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic BASE64(client_id:client_secret)

grant_type=client_credentials
&scope=api.read api.write

Respuesta:

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Pasos 3-4: Usar el token

Igual que antes—incluir el token en las peticiones a la API.

Sin Resource Owner

Nota que no hay usuario en este flujo. El client actúa en su propio nombre, no en nombre de un usuario. El access token representa los permisos del client, no los de un usuario.

Esto es apropiado para:

  • Comunicación entre microservicios
  • Trabajos de procesamiento por lotes
  • Herramientas de administración

5. Flujo Implicit (Obsoleto)

Puede que encuentres el Flujo Implicit en documentación antigua. Fue diseñado para apps basadas en navegador que no podían almacenar secretos de forma segura.

graph LR
    A[App en Navegador] -->|"Solicitud"| B[Auth Server]
    B -->|"Token en fragmento de URL"| A
    style A fill:#ffcdd2
    style B fill:#ffcdd2

Por qué está obsoleto:

  • Access token expuesto en la URL del navegador
  • Vulnerable a fugas de tokens
  • Sin refresh tokens
  • PKCE proporciona una mejor solución

No uses el Flujo Implicit para nuevas aplicaciones. Usa el Flujo Authorization Code con PKCE en su lugar.

6. PKCE Explicado

PKCE (Proof Key for Code Exchange, pronunciado “pixi”) es una extensión que hace que el Flujo Authorization Code sea seguro para clients públicos—aplicaciones que no pueden mantener un secreto.

El Problema que Resuelve PKCE

Las apps móviles y las aplicaciones de página única (SPAs) no pueden almacenar de forma segura un client_secret. Cualquiera puede:

  • Descompilar una app móvil
  • Ver el código fuente de JavaScript

Sin un secreto, ¿qué impide que un atacante intercepte el código de autorización y lo intercambie por tokens?

La Solución: Code Verifier y Challenge

PKCE añade un secreto dinámico para cada solicitud de autorización:

sequenceDiagram
    participant C as Client
    participant AS as Auth Server

    Note over C: Genera un
code_verifier aleatorio Note over C: Crea code_challenge
= SHA256(code_verifier) C->>AS: 1. Solicitud de auth + code_challenge Note over AS: Almacena code_challenge AS->>C: 2. Devuelve código de autorización C->>AS: 3. Solicitud de token + code_verifier Note over AS: Verifica:
SHA256(code_verifier)
== challenge almacenado? AS->>C: 4. Devuelve tokens

Cómo funciona:

  1. El client genera un secreto (code_verifier): Una cadena aleatoria, ej., dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

  2. El client crea un challenge: code_challenge = BASE64URL(SHA256(code_verifier))

  3. La solicitud de autorización incluye el challenge:

https://auth.ejemplo.com/authorize?
  response_type=code
  &client_id=TU_CLIENT_ID
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256
  ...
  1. El intercambio de token incluye el verifier original:
POST /token
grant_type=authorization_code
&code=CODIGO_AUTH
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
  1. El servidor verifica: SHA256(code_verifier) debe coincidir con el challenge almacenado

Por Qué Funciona

Incluso si un atacante intercepta el código de autorización:

  • No tiene el code_verifier
  • El challenge fue hasheado, así que no pueden revertirlo
  • No pueden completar el intercambio de token

PKCE debería usarse para todos los flujos authorization code, no solo para clients públicos. Es una medida de defensa en profundidad.

7. OpenID Connect: Capa de Identidad

OAuth 2.0 responde: “¿Puede esta app acceder a estos recursos?”

Pero no responde: “¿Quién es el usuario?”

OpenID Connect (OIDC) es una capa sobre OAuth 2.0 que añade identidad. Responde: “¿Quién acaba de iniciar sesión?”

graph TB
    subgraph "La Relación"
        OAuth[OAuth 2.0
Autorización] OIDC[OpenID Connect
Identidad + Autorización] end OAuth -->|"Construido encima de"| OIDC style OAuth fill:#fff3e0 style OIDC fill:#e3f2fd

Qué Añade OIDC

  1. ID Token: Un JWT que contiene claims de identidad del usuario
  2. Endpoint UserInfo: Una API para obtener datos del perfil del usuario
  3. Scopes Estándar: openid, profile, email
  4. Claims Estándar: sub, name, email, picture

Cuándo Usar OIDC

  • “Iniciar sesión con Google/GitHub/Facebook”
  • Single Sign-On (SSO)
  • Cualquier momento que necesites saber QUIÉN es el usuario

Cuándo OAuth Simple es Suficiente

  • Acceder a recursos del usuario (fotos, archivos, repositorios)
  • Comunicación máquina a máquina
  • Cualquier momento que solo necesites acceso, no identidad

8. ID Token vs. Access Token

OIDC introduce un nuevo tipo de token: el ID Token. Entender la diferencia entre ID tokens y access tokens es crucial.

Access Token

Propósito: Otorga acceso a recursos

Audiencia: Resource servers (APIs)

Contenido: Puede ser opaco (cadena aleatoria) o un JWT con claims de autorización

Ejemplo de uso:

GET /api/photos
Authorization: Bearer ya29.a0AfH6SM...

Quién lo valida: El resource server

ID Token

Propósito: Prueba la identidad del usuario

Audiencia: Tu aplicación (el client)

Contenido: Siempre un JWT con claims de identidad

Ejemplo decodificado:

{
  "iss": "https://accounts.google.com",
  "sub": "110169484474386276334",
  "aud": "TU_CLIENT_ID",
  "exp": 1704067200,
  "iat": 1704063600,
  "email": "[email protected]",
  "name": "Alice Smith",
  "picture": "https://lh3.googleusercontent.com/..."
}

Quién lo valida: Tu aplicación

Diferencias Clave

AspectoAccess TokenID Token
PropósitoAcceder a recursosIdentificar al usuario
AudienciaResource serverAplicación client
FormatoOpaco o JWTSiempre JWT
Se envía aAPIsNunca se envía a APIs
ValidezMinutos a horasCorta duración

Error Común

Nunca envíes ID tokens a APIs como autorización.

Los ID tokens son para que tu aplicación sepa quién inició sesión. Los access tokens son para que las APIs autoricen peticiones.

# MAL - ID token a la API
GET /api/data
Authorization: Bearer eyJhbGciOiJSUzI1NiIs... (ID token)

# CORRECTO - Access token a la API
GET /api/data
Authorization: Bearer ya29.a0AfH6SM... (Access token)

9. Eligiendo el Flujo Correcto

Aquí hay un árbol de decisión para elegir el flujo OAuth correcto:

flowchart TD
    A[Inicio] --> B{¿Hay un usuario?}
    B -->|No| C[Flujo Client Credentials]
    B -->|Sí| D{¿Tu app puede
mantener un secreto?} D -->|Sí| E[Flujo Authorization Code] D -->|No| F[Flujo Authorization Code
+ PKCE] E --> G{¿Necesitas identidad?} F --> G G -->|Sí| H[Añadir OpenID Connect] G -->|No| I[OAuth 2.0 simple] style C fill:#fff3e0 style E fill:#e3f2fd style F fill:#e8f5e9 style H fill:#fce4ec

Tabla Resumen

EscenarioFlujo¿OIDC?
App web con backendAuthorization CodeOpcional
App de página única (SPA)Authorization Code + PKCEGeneralmente sí
App móvilAuthorization Code + PKCEGeneralmente sí
Servicio backendClient CredentialsNo
Botón “Iniciar sesión con X”Authorization Code (+ PKCE)

Recomendaciones Modernas

  1. Siempre usa PKCE para flujos authorization code, incluso si tienes un backend
  2. Nunca uses el Flujo Implicit para nuevas aplicaciones
  3. Usa OIDC cuando necesites saber quién es el usuario
  4. Usa OAuth simple cuando solo necesites acceder a recursos

10. Qué Viene Después

Ahora entiendes la base conceptual de OAuth 2.0 y OpenID Connect. Sabes:

  • Por qué existe OAuth (autorización delegada)
  • Los cuatro roles (resource owner, client, authorization server, resource server)
  • Cómo funcionan los flujos principales (authorization code, client credentials)
  • Por qué existe PKCE (seguridad para clients públicos)
  • La diferencia entre OAuth y OIDC (autorización vs. identidad)
  • Cuándo usar ID tokens vs. access tokens

Lo que Esta Guía No Cubrió

Esta guía intencionalmente evitó:

  • Detalles de implementación: Cómo implementar OAuth de forma segura en tu lenguaje/framework
  • Vulnerabilidades comunes: CSRF, fugas de tokens, manipulación de redirect URI
  • Almacenamiento de tokens: Dónde y cómo almacenar tokens de forma segura
  • Rotación de refresh tokens: Manejar la expiración de tokens de forma segura
  • Flujos avanzados: Device Authorization, Token Exchange

Estos temas requieren un estudio más profundo y práctica hands-on.

Próximos Pasos Recomendados

Aprendizaje práctico:

  • Intenta implementar OAuth con un proveedor (Google, GitHub, Auth0)
  • Usa una librería bien probada (nunca hagas tu propio OAuth)
  • Estudia las especificaciones RFC (listadas abajo)

Vista Previa del Curso:

OAuth 2.0 Bien Hecho (y Mal Hecho)

Un curso práctico que cubre:

  • Patrones de implementación seguros
  • Escenarios de ataque del mundo real
  • Estrategias de almacenamiento de tokens
  • Errores de configuración comunes
  • Depuración de flujos OAuth

Próximamente en API/OS

Términos del Vocabulario Relacionados

Profundiza tu comprensión explorando estos términos:

Estándares y RFCs


Has construido un modelo mental sólido de OAuth 2.0 y OpenID Connect. El siguiente paso es la implementación práctica—pero ahora entenderás qué estás implementando y por qué.