Terminal Auth Local del Agent · Fase 2 (frontend, token opcional)
Descripción
Segundo paso de la arquitectura de seguridad del Agent local. Complementa a Fase 1 (backend).
La web ahora manda el token de auth local automáticamente a cada llamada fetch dirigida al Agent (localhost:5050 / 127.0.0.1:5050), sin necesidad de modificar los ~35 call-sites del código cliente uno a uno.
Característica clave: Token OPCIONAL
El token es completamente opcional en esta fase. Si no existe aún (porque el Agent aún no lo deposita hasta Fase 3, o no hay sesión de usuario), la llamada se emite sin header Authorization y se comporta idénticamente al estado actual. Por eso este cambio es totalmente inocuo para los .exe antiguos y para las webs sin sesión.
No da seguridad por sí sola — es fontanería. El candado real (Agent .exe exigiendo y validando el token, cerrando CORS) llega en Fase 3.
Cómo funciona
Intercepción de window.fetch
Se carga temprano en base.html el módulo static/js/modules/agent_auth.js. Este módulo:
-
Obtención del token (una sola vez por carga de página, memoizado):
- Pregunta al Agent local su identidad:
GET http://localhost:5050/info→ extraeagent_id - Consulta el SaaS:
GET /api/agent/local-token?agent_id=... - Solo responde si hay sesión +
fleet:view+ el agent pertenece al tenant del usuario - Si falla en cualquier paso, devuelve
null(no rompe nada)
- Pregunta al Agent local su identidad:
-
Reescritura de
window.fetch: Intercepta ALL llamadas fetch. Si el URL es de Agent y hay token, añadeAuthorization: Bearer <token>. Si no hay token, la llamada pasa como antes.
Casos especiales (no son fetch simples)
Consola SSH (iframe)
El iframe (ssh_client.js) recibe el token por postMessage con tipo SET_AUTH_TOKEN antes de recibir la configuración SSH. El targetOrigin está acotado al Agent local.
- Un Agent viejo que no entiende el mensaje lo ignora (inocuo).
- El nuevo lo usa para autenticar el WebSocket SSH.
Descargas de logs
Migradas de window.open() (no lleva headers) a fetch+blob (window.downloadFromAgent()):
- En
TerminalToolbar.js: descarga al parar grabación - En
ssh_integration.js: descarga al cerrar sesión SSH
La función downloadFromAgent() pasa por window.fetch interceptado, así que el token se añade automáticamente.
Cambios en el código
| Archivo | Cambio |
|---|---|
static/js/modules/agent_auth.js | Nuevo. 96 LOC. Interceptor + token fetch + descarga |
static/js/modules/ssh_client.js | Añade postMessage SET_AUTH_TOKEN antes de config SSH |
static/js/editor/ssh_integration.js | Sustituye window.open() por downloadFromAgent() |
static/js/terminal/TerminalToolbar.js | Sustituye window.open() por downloadFromAgent() |
templates/base.html | Carga agent_auth.js temprano (defer, antes de módulos) |
CHANGELOG.md | Entrada Fase 2, con ADR ref |
RELEASE_NOTES.md | s125: Agente local más seguro, paso 2 |
Dependencias y relaciones
- ADR:
decision--20260610--terminal-auth-local-cross-origin— arquitectura + token spec - Fase anterior:
feature--terminal--auth-local-fase-1(backend/api/agent/local-token) - Entidad técnica:
entity--static--module--agent-auth(el módulo interceptor) - Cliente:
entity--static--module--ssh-client,entity--static--class--TerminalToolbar - Concepto: [[concept—security—cross-origin-local-auth]]
Testing
⚠️ No cubierto por CI — requiere smoke manual en navegador con el Agent .exe corriendo localmente.
Pruebas recomendadas:
- Con Agent viejo (sin token en
/info): todas las llamadas siguen funcionando sin token. - Con Agent Fase 2 (retorna token): verifica que el header
Authorization: Bearer <token>se añade. - Descarga de logs: confirma que blob se crea y descarga correctamente.
- Consola SSH (iframe): verifica que el token llega a tiempo antes del WS.
Véase también
- [[decision—20260610—terminal-auth-local-cross-origin]]
- [[entity—static—module—agent-auth]]
- [[entity—static—module—ssh-client]]
- [[feature—terminal—auth-local-fase-1]]
- [[concept—security—cross-origin-local-auth]]