Flujos de OAuth 2.0 Código de autorización, PKCE y credenciales del cliente
Una guía para desarrolladores para elegir el flujo adecuado de OAuth 2.0. Aborda el flujo de autorización (aplicaciones web), PKCE (aplicaciones de página única y móviles) y credenciales del cliente (intercambio entre servidores), con ejemplos de código funcionales y los errores que te atrapan más adelante.
Tres flujos cubren 95% de casos reales de uso de OAuth 2.0. La especificación define más, pero el resto están desactualizados, en casos extremos o ambos. Elige el adecuado desde el principio y evitas un refactoring doloroso cuando cambian los requisitos de autenticación.
| Flujo | Cuándo usarlo | ¿El usuario está involucrado? | ¿Se necesita client_secret? |
|---|---|---|---|
| Código de autorización | Aplicación web con servidor backend | Sí | Sí — permanece en el servidor |
| Código de autorización + PKCE | Aplicación de página única (SPA), aplicación móvil, cualquier cliente público | Sí | No |
| Credenciales del cliente | Interacción entre servidores (sin usuario) | No | Sí — permanece en el servidor |
Flujo de Código de Autorización
El flujo estándar para aplicaciones web con un servidor backend. El token de acceso y el secreto del cliente nunca tocan el navegador — la intercambio de tokens ocurre en el servidor. Eso es el punto principal.
Paso 1: Redirigir al usuario
Construye una URL de autorización y redirige el navegador a ella:
GET https://accounts.example.com/oauth/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.com/callback
&scope=openid+profile+email
&state=RANDOM_CSRF_TOKEN
El state el valor es tu defensa contra CSRF en la redirección. Genera una cadena aleatoria fresca por flujo, guárdala en la sesión y verifica cuando el usuario regrese. Si omites esta verificación, un atacante puede hacer que el usuario siga un flujo usando su propio código de autorización — vinculando silenciosamente la cuenta del usuario a la identidad del atacante.
Paso 2: Manejar la respuesta de redirección
El servidor de autorización redirige de regreso a tu redirect_uri con un código temporal:
GET https://yourapp.com/callback?code=AUTH_CODE&state=SAME_STATE_YOU_SENT
Verificar state coincide con lo que almacenaste. Luego intercambia el código en el servidor.
Paso 3: Intercambiar el código por tokens (solo en el servidor)
POST https://accounts.example.com/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https://yourapp.com/callback
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
La respuesta:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "def50200a12b3c...",
"scope": "openid profile email"
}
Guárdalos ambos en el servidor. Cuando access_token vence (verifica expires_in), utiliza el refresh_token para obtener uno nuevo sin que el usuario vuelva a iniciar sesión.
PKCE — Para clientes que no pueden mantener secretos
Una SPA o una aplicación móvil no tiene un lugar seguro para un client_secret. Cualquiera puede abrir DevTools y encontrarlo en tu paquete JS. Cualquiera puede descompilar tu APK. PKCE (Proof Key for Code Exchange, pronunciado "pixy") resuelve esto con un desafío criptográfico de una sola vez — sin necesidad de un secreto compartido.
El flujo es idéntico al de Código de Autorización con dos añadidos: un code_verifier (cadena aleatoria que generas) y un code_challenge (hash SHA-256 del verificador, codificado en base64url). Envías el desafío al inicio y demuestras que posees el verificador al momento del intercambio.
Paso 1: Generar el verificador y el desafío
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64url(array);
}
async function generateCodeChallenge(verifier) {
const data = new TextEncoder().encode(verifier);
const digest = await crypto.subtle.digest('SHA-256', data);
return base64url(new Uint8Array(digest));
}
function base64url(buffer) {
return btoa(String.fromCharCode(...buffer))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
// Usage
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
// Store codeVerifier in memory — NOT localStorage
Guárdalo en memoria — una variable de alcance de módulo, no en localStorage. Lo enviarás al momento del intercambio de tokens. code_verifier Paso 2: Solicitud de autorización — añade el desafío
Paso 3: Intercambio de tokens — verificador en lugar del client_secret
GET https://accounts.example.com/oauth/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.com/callback
&scope=openid+profile
&state=RANDOM_CSRF_TOKEN
&code_challenge=BASE64URL_SHA256_OF_VERIFIER
&code_challenge_method=S256
El servidor de autorización hash el verificador y lo compara con el desafío que enviaste en el paso 2. Un atacante que interceptó el código de autorización no sabe qué es el verificador — no puede intercambiarlo.
POST https://accounts.example.com/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https://yourapp.com/callback
&client_id=YOUR_CLIENT_ID
&code_verifier=ORIGINAL_VERIFIER_YOU_GENERATED
Valor a tener en cuenta: OAuth 2.1 (la modernización en curso de OAuth 2.0) exige PKCE para todos los flujos que implican redirecciones. Si estás escribiendo código nuevo, utiliza PKCE independientemente de si tu proveedor lo requiere actualmente.
Credenciales del cliente — Sin usuario, sin problema
Tareas en segundo plano, microservicios que llaman a otros microservicios, tareas cron que acceden a una API — ninguno de estos involucra a un usuario. El flujo adecuado es el de credenciales del cliente: el servicio se autentica directamente usando su propio ID y secreto del cliente.
Respuesta:
POST https://accounts.example.com/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&scope=api:read api:write
Sin token de refresco — cuando vence, simplemente solicita uno nuevo. El error común: hacer una llamada al punto de token en cada solicitud de API. Almacena el token, verifica la expiración antes de cada solicitud, y solo solicita una actualización cuando se acerque a vencer. Una solicitud de token por día (o por hora, según
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 86400
}
) en lugar de una por cada solicitud: expires_inErrores que cometen realmente los desarrolladores
let cachedToken = null;
let tokenExpiresAt = 0;
async function getAccessToken() {
// Refresh 30 seconds before actual expiry
if (cachedToken && Date.now() < tokenExpiresAt - 30_000) {
return cachedToken;
}
const res = await fetch('https://accounts.example.com/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
scope: 'api:read api:write',
}),
});
const data = await res.json();
cachedToken = data.access_token;
tokenExpiresAt = Date.now() + (data.expires_in * 1000);
return cachedToken;
}
Almacenar tokens en localStorage
Cualquier vulnerabilidad XSS — en tu propio código, en una dependencia, en un script de gestor de etiquetas — puede leer todo lo que está en localStorage. Para SPAs: almacena el token de acceso en memoria (una variable de alcance de módulo que desaparece al refrescar). Usa cookies httpOnly para tokens de refresco cuando tengas un backend que pueda establecerlas. JavaScript no puede leer cookies httpOnly.
Usar el flujo implícito
El flujo implícito devuelve tokens directamente en el fragmento de URL (
). Estos tokens terminan en el historial del navegador, en los logs de acceso del servidor y en los encabezados Referer. Fue descontinuado en RFC 9700. No hay razón para usar el flujo implícito en código nuevo. Usa PKCE.#access_token=...Omitir la validación de estado
La validación en la respuesta de redirección, un atacante puede crear una URL de redirección que complete un flujo de OAuth usando su propio código de autorización. El resultado: la cuenta del usuario se vincula a la identidad del atacante en el proveedor. Genera uno nuevo por flujo, guárdalo en la sesión y verifica en la respuesta de redirección.
Pega el contenido de tu .github/workflows/*.yml aquí state Colocar el client_secret en el código del frontend
No existe tal cosa como un secreto que viva en un navegador. La minimización no lo oculta. La obfuscación no lo protege. Si tu entorno es un navegador o una aplicación móvil, tienes un
cliente público — utiliza PKCE y omite completamente el client_secret. Eso no es una solución de trabajo; es cómo el especificación pretende que funcionen los clientes públicos. No manejar la expiración del token proactivamente
Cada token de acceso tiene un
valor. Si mantienes un token hasta que falle con un 401 y luego vuelves a autenticar, los usuarios enfrentan errores misteriosos. Verifica la expiración antes de hacer solicitudes, actualiza proactivamente (30 segundos antes de la expiración es un margen razonable) y maneja el caso raro en el que el token de refresco mismo ha expirado. expires_in Inspeccionar tokens mientras trabajas
La mayoría de los proveedores de OAuth emiten JWT como tokens de acceso. El payload está codificado en base64url y es legible sin la clave privada — solo la firma requiere la clave para verificar. Cuando estás depurando un flujo y quieres ver las declaraciones, los alcances o la expiración en un token, pégalo en el
Si estás probando manualmente PKCE y necesitas verificar qué decodifica una cadena base64url codificada, el Decodificador JWT.
Convertidor de base64 code_challenge maneja variantes estándar y seguras para URLs. Una pregunta determina el flujo adecuado: ¿tu entorno tiene un lugar seguro para mantener un secreto? Servidor backend
La versión breve
→ Flujo de Código de Autorización o Credenciales del Cliente (el secreto permanece en el servidor)
- Navegador o aplicación móvil → Código de Autorización + PKCE (sin secreto en absoluto)
- Sin usuario involucrado → Credenciales del Cliente
- PKCE funciona para todos los flujos que implican una redirección — no hay inconveniente en usarlo incluso cuando tu proveedor aún no lo requiere. OAuth 2.1 lo exigirá. Flujos de OAuth 2.0: Código de Autorización, PKCE y Credenciales del Cliente 2
Flujos de OAuth 2.0: Código de Autorización, PKCE y Credenciales del Cliente 1
Instalar extensiones
Agregue herramientas IO a su navegador favorito para obtener acceso instantáneo y búsquedas más rápidas
恵 ¡El marcador ha llegado!
Marcador es una forma divertida de llevar un registro de tus juegos, todos los datos se almacenan en tu navegador. ¡Próximamente habrá más funciones!
Herramientas clave
Ver todo Los recién llegados
Ver todoActualizar: Nuestro última herramienta was added on Jun 23, 2026
