1 · El token de acceso

Un JWT: tres partes en base64url separadas por puntos — cabecera, contenido y firma EdDSA. No está cifrado: cualquiera que lo tenga lo lee, y cualquiera que lo tenga lo usa. Sólo la firma impide fabricarlo. Esta página lo decodifica pero no verifica la firma: la llave pública (JWKS) no se publica por el borde de dev, y quien verifica es el gateway.

cabecera

contenido

claimvalorqué significa

2 · El token de renovación

scope=renew: sólo sirve en POST /security/auth/renew, que devuelve un par NUEVO. Rota en cada uso (el viejo deja de valer) y caduca a los 7 días sin usarse. auth vuelve a leer al usuario en la base al renovar: una cuenta desactivada no renueva. El token nuevo ya no lleva amr.

3 · Dónde se guarda

Una página estática no tiene servidor que ponga cookies HttpOnly, así que las pone JavaScript y JavaScript las puede leer. La defensa es otra: la política de seguridad del servidor (script-src 'self', sin scripts en línea ni de terceros) y que ningún dato se inserte como HTML. Las cookies son del host (sin Domain): simplest.instances… no ve esta sesión aunque use los mismos nombres.

dóndeclavecontenidovidapara qué

Atributos que escribe js/auth.js en cada cookie: Path=/; SameSite=Strict; Secure; Max-Age=…. El navegador no deja leerlos de vuelta: sólo nombre y valor. Nunca se guardan la contraseña, el código del correo ni el code de Google.

4 · Qué ve el borde con este token

GET /memberships/me con Authorization: Bearer …. El gateway verifica firma, caducidad y scope; borra cualquier X-Subject que mande el cliente y lo vuelve a poner desde sub. El servicio sólo ve esa cabecera.

5 · El token del stream

POST /security/auth/sse-token con el token de acceso devuelve otro, scope=sse, 600 s. Va en la URL (/sse?token=…) porque EventSource no puede mandar cabeceras; por eso es aparte y sólo abre el stream.

6 · Ver como

No toca tu sesión: pide un SEGUNDO token (POST /impersonation/view-as/token), 300 s, con sub = tú e impSub = el otro usuario. El gateway lo aplica: X-Subject = el otro, X-Superuser = false, X-Acting-User = tú, y sólo acepta lecturas. Vive en sessionStorage (sólo esta pestaña). Salir es tirarlo: tu token nunca cambió.

7 · Cómo se llegó aquí: la traza de este login

Cada llamada que hizo esta pestaña para entrar: método, ruta, estado, tiempo y el requestId con el que se busca en la auditoría. Nunca un cuerpo.

+msllamadaestadoqué pasarequestId

8 · Las cuatro maneras de entrar, lado a lado

contraseñacódigo / enlaceGoogleproveedor OIDC
qué pruebasque sabes un secretoque controlas el buzónque Google te autenticóque tu empresa te autenticó
llamadasPOST /loginPOST /code/request → correo → POST /code/verify navegador → Google → vuelve con code → POST /google {code, redirectUri} navegador → /federated/{idp}/start → proveedor → callback de auth → vuelve con handoff → POST /handoff
qué guarda authhash PBKDF2 en users.user_credentialhash del código en users.login_code, 10 min, 5 intentos el sub de Google, ligado por correo verificado la primera vezel sub del proveedor; state, nonce y PKCE 10 min en memoria
quién tiene el secretotúnadie: el código muere al usarseauth (secreto del cliente OAuth)auth (secreto del cliente, en Secret Manager)
crea usuariosno — sólo entran cuentas que Tagora ya dio de alta (AUTH_JIT_PROVISION=false)
error401 único para todola petición responde 200 para cualquier correo; el canje, 401 único401 account not registeredvuelve con ?error=…
amrpasswordcodegooglehandoff
termina enLoginHandlers.issue(): el mismo par de tokens, firmado con la misma llave

9 · Después del login: autenticación y autorización en cada petición

  1. Autenticación, en el gateway. Firma EdDSA contra el JWKS de auth, exp, y que el scope sirva para la ruta. Falla → 401. Un token de integración en una ruta de primera parte, o al revés → 403 forbidden scope.
  2. Identidad. El gateway borra X-Subject, X-Scope, X-Superuser, X-View-As, X-Act-As y X-Acting-User que vengan del cliente y los vuelve a escribir desde el token. Ningún servicio vuelve a mirar el token.
  3. PEP, en el servicio. Cada ruta está en un manifiesto (route_manifest.tsv): qué permiso, sobre qué tipo, con qué id. Ruta sin política → 403 no policy for route.
  4. PDP (mstar-authz → SpiceDB). ¿Tiene user:<sub> el permiso sobre ese objeto? No → 403. No se pudo preguntar → 503. Las listas se filtran con un solo CheckBulk: lo que no puedes ver no aparece (un 200 más corto, no un error).
  5. Datos. Funciones SECURITY DEFINER en Postgres; las escrituras registran quién las hizo (y, bajo ver-como/actuar-como, acting_user_id).

El método de login no cambia nada de esto: el gateway, el PEP y el PDP no distinguen un token de contraseña de uno de Google. Sólo la auditoría ve el amr. Un superusuario se salta el PEP; bajo ver-como nunca lo es.