Contenido del curso

PKCE con OAuth 2.0 en la API de Twitter

Resumen

Si estás construyendo una aplicación que no puede guardar secrets de forma segura, necesitas Authorization Code Flow con Proof Key for Code Exchange (PKCE) en la API de Twitter. Esta variante del flujo OAuth 2.0 protege a single page apps, apps nativas y mobile frente a la intercepción de códigos de autorización.

La idea central es simple: añades dos piezas criptográficas (un code verifier y un code challenge) que prueban que quien intercambia el código es el mismo cliente que inició la solicitud.

¿Qué configuración necesitas en el portal de Twitter Developer?

Antes de tocar código, debes registrar tu aplicación en developer.twitter.com y obtener las credenciales correctas.

Dentro del portal puedes crear un proyecto y hasta tres aplicaciones por proyecto. En overview, agregas una nueva app, eliges el ambiente (por ejemplo staging) y le das un nombre descriptivo como auth code flow with PKCE Twitter [01:05].

Al crear la app no verás de inmediato el client ID ni el client secret. Para que aparezcan, debes entrar a user authentication settings y configurar:

  • Permisos de la app: en este caso solo lectura, sin acceso al email del usuario.
  • Tipo de aplicación: native app, es decir, un cliente público.
  • URL de callback: en desarrollo usamos http://localhost:3003/api/callback.

¿Qué es un cliente público en OAuth? Es una aplicación que no puede guardar credenciales de forma segura, como una single page app o una app móvil. Por eso necesita PKCE en lugar de un client secret tradicional.

Una vez guardas la configuración, copias el client ID y el client secret. Guarda el secret en un lugar seguro porque Twitter no te lo mostrará de nuevo y tendrás que regenerarlo [02:30].

¿Cómo se diferencia PKCE del Authorization Code Flow tradicional?

El flujo es casi idéntico al authorization code flow clásico, pero con un detalle clave que blinda la seguridad.

Antes de redirigir al usuario al authorization server, tu cliente genera dos valores:

  • Code verifier: un string aleatorio de 128 bits.
  • Code challenge: el resultado de aplicar un hash SHA-256 al code verifier.
  • Code challenge method: el algoritmo usado, en este caso S256.

En la solicitud inicial a /authorize envías el code_challenge y el code_challenge_method. Más adelante, al canjear el código por el token en /token, envías el code_verifier original.

¿Por qué PKCE necesita un hash irreversible? Porque el code challenge viaja por la red en la primera petición. Si un atacante lo intercepta, no puede deducir el code verifier original, así que no puede completar el intercambio.

Esta validación cruzada garantiza que el cliente que recibe el token es el mismo que inició el flujo [04:10].

¿Cómo se construye el authorization request en el código?

El punto de partida es el endpoint https://twitter.com/i/oauth2/authorize. Cada API tiene su propia URL, pero todas siguen el mismo estándar OAuth 2.0.

Los parámetros que envías son:

  • response_type=code.
  • client_id desde las variables de entorno.
  • scope con los permisos necesarios (leer datos del usuario y sus tuits).
  • redirect_uri igual al registrado en el portal.
  • state: un string aleatorio de 16 bits para prevenir ataques de cross-site request forgery.
  • code_challenge y code_challenge_method=S256.

Para generar valores aleatorios se usa la utilidad randomstring, y para el hash una función propia llamada generateCodeChallenge que recibe el code verifier.

Un detalle frecuente de error: las APIs piden los parámetros en snake_case (code_challenge, no codeChallenge). Si los escribes en camelCase, recibirás un error tipo code_challenge is not defined [06:45].

¿Dónde se almacenan el state y el code verifier?

Como ambos valores deben compararse después en el callback, se guardan en cookies del navegador. Puedes escribirlas como un array para incluir varias cookies en una sola respuesta: una para el state y otra para el code_verifier.

¿Cómo se implementa el callback que intercambia el código por un token?

Cuando el usuario autoriza la app, Twitter redirige a tu redirect_uri con un code y el state original en el query string.

El callback debe ejecutar dos validaciones y un intercambio:

  1. Verificar que request.query.state sea igual a cookies.state. Si no coinciden, muestras un error y detienes el flujo.
  2. Construir la petición POST al endpoint /token con grant_type=authorization_code, client_id, el code recibido y el code_verifier recuperado de la cookie.
  3. Recibir el access_token y guardarlo en una cookie segura para usarlo en futuras peticiones.

A partir de ese momento puedes llamar a la API de Twitter incluyendo el header Authorization: Bearer <access_token> para obtener información del usuario o sus últimos tuits [09:20].

¿Cómo se evita el bloqueo CORS al llamar a la API de Twitter?

Twitter aplica políticas de CORS que impiden hacer requests directos desde el cliente. La solución es crear un endpoint intermedio en el backend.

Usando las Route APIs de Next.js puedes crear un endpoint llamado /api/cors que actúa como middleware: recibe la URL objetivo, hace la llamada con el access token y devuelve la respuesta al cliente, que la renderiza con componentes de React.

Reto práctico para fijar el flujo

Implementa Authorization Code Flow con PKCE en un servicio distinto a Twitter. Algunas opciones interesantes son Spotify, GitHub o Google. Compara cómo cada API nombra sus scopes, qué endpoints expone y cómo manejan el redirect uri.

¿Qué servicio elegiste y qué diferencias encontraste frente al flujo de Twitter? Cuéntalo en los comentarios.

      PKCE con OAuth 2.0 en la API de Twitter