¿Odias los anuncios? Ir Sin publicidad Hoy

Flujos de OAuth 2.0 Código de autorización, PKCE y credenciales del cliente

Actualizado en

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.

Flujos de OAuth 2.0: Código de autorización, PKCE y credenciales del cliente 1
ANUNCIO · ¿ELIMINAR?

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.

FlujoCuándo usarlo¿El usuario está involucrado?¿Se necesita client_secret?
Código de autorizaciónAplicación web con servidor backendSí — permanece en el servidor
Código de autorización + PKCEAplicación de página única (SPA), aplicación móvil, cualquier cliente públicoNo
Credenciales del clienteInteracción entre servidores (sin usuario)NoSí — 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

¿Quieres eliminar publicidad? Adiós publicidad hoy

Instalar extensiones

Agregue herramientas IO a su navegador favorito para obtener acceso instantáneo y búsquedas más rápidas

añadir Extensión de Chrome añadir Extensión de borde añadir Extensión de Firefox añadir Extensión de Opera

¡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!

ANUNCIO · ¿ELIMINAR?
ANUNCIO · ¿ELIMINAR?
ANUNCIO · ¿ELIMINAR?

Noticias Aspectos técnicos clave

Involucrarse

Ayúdanos a seguir brindando valiosas herramientas gratuitas

Invítame a un café
ANUNCIO · ¿ELIMINAR?