El ida y vuelta del login
El recorrido completo cruza dos servicios y tres dominios, y entenderlo entero es lo que permite depurarlo cuando falla a mitad.
1. navegador → panel protegido (no tiene sesión)
2. panel → /auth/google con ?redirect_uri=…
3. identity → Google con state, y dos cookies guardadas
4. Google → /auth/google/callback con ?code&state
5. identity → valida, crea sesión y redirige al panel con ?token=
6. panel → verifica el token y pone su propia cookie
7. navegador → panel (ya con sesión)
1-2 · Nadie tiene destino por defecto
El panel manda al usuario a /auth/google y está obligado a decir a dónde volver:
const redirectUri = req.query.redirect_uri as string | undefined;
if (!redirectUri) return res.status(400).json({ error: "redirect_uri is required" });
if (!config.allowedRedirectUris.includes(redirectUri))
return res.status(400).json({ error: "redirect_uri is not allowed" });
No hay frontend por defecto al que caer: si el redirect_uri falta o no está en la allowlist, el
flujo muere aquí con un 400. Por qué se valida así, en
las dos defensas.
3 · Dos cookies de diez minutos
Antes de mandar al usuario a Google se guardan dos cookies, ambas httpOnly y con diez minutos de
vida: oauth_state (el testigo anti-CSRF) y oauth_redirect (a dónde volver). Son lo único que ata
la vuelta de Google con el navegador que empezó.
4-5 · La vuelta
En el callback se comprueban las dos cosas —el state coincide y el destino sigue siendo válido— y
las cookies se borran antes de decidir nada, hayan salido bien o mal. Después se canjea el
code con Google, se verifica el id_token, y se crea o recupera el usuario.
Aquí se emite la sesión propia: un access token JWT de 15 minutos firmado con
IDENTITY_JWT_SECRET, y un refresh token opaco de 40 bytes que se guarda hasheado con SHA-256 y
viaja en una cookie httpOnly de 7 días.
5 · El token viaja en la URL, y por qué
Es la parte que más llama la atención:
target.searchParams.set("token", accessToken);
res.redirect(target.toString());
El motivo está comentado en el código: el consumidor está en otro dominio y no puede leer la
cookie httpOnly de este servidor. La única forma de entregarle la sesión es adjuntando el access
token a la propia redirección.
Por eso el access token dura 15 minutos y por eso se espera que el consumidor redirija otra vez inmediatamente después de guardarlo: así el token no se queda en el historial del navegador.
6 · La parte que ocurre en el otro repo
El consumidor no confía en el token por venir de donde viene: lo verifica otra vez antes de guardarlo, y lo cambia por una cookie propia. Eso es proteger un panel.
Dónde falla, y qué significa
| Síntoma | Dónde | Causa habitual |
|---|---|---|
400 redirect_uri is not allowed |
paso 2 | falta en ALLOWED_REDIRECT_URIS, o no coincide exacto |
400 Invalid or missing OAuth state |
paso 4 | la cookie caducó (más de 10 min), o el navegador no la devolvió |
400 Missing or expired redirect_uri |
paso 4 | misma causa, la otra cookie |
500 Authentication failed |
paso 4 | falló el canje con Google: credenciales o GOOGLE_REDIRECT_URI mal |
| Entra y al rato se cae la sesión | paso 7 | normal si el servicio se reinició — las sesiones viven en memoria |
Ese último es el que más despista, y no es un fallo: ver qué es, y qué no es.