Módulo 4 · Autenticación y seguridad

Lección 19 — OAuth2 y OpenID Connect

Flujos, scopes y por qué «conectar con Google» es más difícil de lo que parece.

Publicada
En esta lección
  1. Ejercicio 1 — El flujo, observado
  2. Ejercicio 2 — El id_token bajo la lupa
  3. Ejercicio 3 — Account linking (el hoyo clásico)
  4. Ejercicio 4 — Tu API con scopes (adelanto 21)
  5. Ejercicio 5 — ADR-0009: "Login con Google (OIDC)"
  6. Ejercicio 6 — Scopes y el mapa de flujos
  7. Entrega

Cuenta de Google + proyecto en Google Cloud Console (credenciales OAuth gratuitas). No mires solutions.md hasta entregar.

Ejercicio 1 — El flujo, observado

  1. Configura django-allauth con proveedor Google (client_id/secret en settings por entorno — 27). Arranca y haz el login completo desde tu app.
  2. Durante el flujo, captura (DevTools → Network): la URL de /authorize (¿está el code_challenge y el state?), el redirect con ?code=, y la llamada de canje. Enumera los 6 pasos de la lección vistos en tu propio tráfico.
  3. Cambia el state en el callback a mano y repite: ¿qué error? ¿Qué ataque evita esa validación?

Ejercicio 2 — El id_token bajo la lupa

  1. Copia el id_token a jwt.io: lista los claims (iss, aud, sub, email, email_verified, exp, iat, nonce).
  2. Verifica la firma contra el JWKS público de Google (https://www.googleapis.com/oauth2/v3/certs): ¿qué clave corresponde por kid?
  3. Lista los 4 checks mínimos que haría tu backend además de la firma, y qué ataque evita cada uno.

Ejercicio 3 — Account linking (el hoyo clásico)

  1. Escenario A: usuario registrado con email+password (email verificado). Luego entra con Google con EL MISMO email verificado. ¿Qué debe pasar? Implementa el enlazado.
  2. Escenario B: email de Google no verificado (proveedor que no verifica) con el mismo email: ¿qué hace tu código? Implementa la regla: nunca enlazar sin email_verified=true.
  3. Escenario C: mismo humano, dos proveedores (Google + GitHub con emails distintos): ¿dos cuentas? ¿mechanismo de unificación manual? Decide y registra en el ADR.

Ejercicio 4 — Tu API con scopes (adelanto 21)

  1. Si TicketFlow expusiera API a integradores: define 3 scopes (events:read, tickets:write, refunds:write) y el check en tus viewsets (has_permission por scope).
  2. Escribe la respuesta 403 con el scope que faltó (formato RFC 7807, type: missing-scope).

Ejercicio 5 — ADR-0009: "Login con Google (OIDC)"

Contexto (fricción de registro, verificación de email gratis), decisión (allauth + OIDC + linking por email verificado), alternativas (solo password+MFA, magic links), consecuencias (dependencia del proveedor solo en el login, JWKS caching, cuenta local siempre).

Ejercicio 6 — Scopes y el mapa de flujos

  1. Implementa los 5 scopes de TicketFlow (§6) en tu IdP fake y añade el middleware de verificación de scope por endpoint: el checkout exige tickets:purchase, el panel del organizador organizer:*. Test: token sin el scope → 403 con problem+json (26) que dice QUÉ scope faltó.
  2. El consentimiento humano: escribe la pantalla (o el JSON) del consentimiento que el usuario ve: cada scope traducido a lenguaje humano (51: la traducción bidireccional). ¿"tickets:purchase" cómo se lo explicas a tu tía?
  3. El flujómetro: clasifica 4 integraciones reales de tu proyecto en la tabla del §5 (el worker→pasarela, el login del comprador, el terminal físico de taquilla, la SPA vieja que heredaste). ¿Cuál exige migración y por qué?

Entrega

Pega claims, checks y decisiones de linking. Después: Lección 20 — Hash de contraseñas y MFA.