Endpoint: POST /api/agent/register-local-token (Agent deposita token local cifrado)
Resumen
Endpoint que permite al Agent depositar de forma segura su token de autenticación para la API HTTP local (localhost:5050). El token se cifra con CredentialManager (Fernet) antes de guardarse en AgentInstance.local_token_enc.
Inerte en Fase 1: ningún cliente lo usa hasta la Fase 2 (frontend) y Fase 3 (Agent .exe).
Especificación
Ruta
POST /api/agent/register-local-token
Autenticación
auth=None(endpoint no protegido por portero global).- Autoverificación: extrae el Agent JWT del header
Authorization: Bearer <token>usandoget_agent_from_request(). - El
agent_idse toma SIEMPRE del token, nunca del body (previene spoofing).
Request Body
{
"local_token": "string" // token opaco generado por el Agent (p.ej. UUID o JWT local)
}
Response
200 OK (éxito):
{
"status": "stored"
}
400 Bad Request (falta local_token):
{
"error": "Missing local_token"
}
401 Unauthorized (JWT inválido/expirado, o refresh token en lugar de access token):
{
"error": "Invalid or expired token"
}
404 Not Found (Agent no existe en BD):
{
"error": "Agent not found"
}
Implementación
Fichero
terminal/api/auth.py (líneas ~220–237, commit 61b0447).
Sub-componentes
-
get_agent_from_request(request)— helper compartido que extrae y valida el Agent JWT.- Rechaza refresh tokens (solo access tokens autentican datos del Agent).
- Devuelve dict con
agent_id,org_id, etc., oNonesi es inválido.
-
CredentialManager.encrypt_credential(local_token)— cifra con Fernet.- Fuente:
core.security.credential_manager(Plan Hardening E). - Garantiza que el token nunca se guarda en claro.
- Fuente:
-
Salvaguarda en base de datos:
agent.local_token_enc = ...+agent.save(update_fields=[...]).- Actualiza solo
local_token_encyupdated_at(efficient update).
- Actualiza solo
-
Logging:
logger.info(f"Local token registered for agent {agent_id}").- Auditoría sin exponer el token.
Flujo
- Agent genera un token local (UUID o JWT firmado localmente con su keypair).
- Agent obtiene su access token JWT (via
POST /api/agent/autho mantiene uno en sesión). - Agent envía
POST /api/agent/register-local-tokencon:- Header:
Authorization: Bearer <access_token> - Body:
{"local_token": "<opaco>"}
- Header:
- Servidor extrae
agent_iddel JWT. - Servidor busca
AgentInstance(agent_id=...). - Servidor cifra el token y lo guarda en
local_token_enc. - Servidor responde
200 OK.
Seguridad
- No almacenamiento en claro: Fernet (simétricamente cifrado con
CREDENTIAL_ENCRYPTION_KEY). - Prevención de spoofing:
agent_idextraído del token JWT, no del body. - Sin persistencia de refresh tokens: solo access tokens autenticas datos.
- Logging seguro: no registra el token, solo el event.
Testing
Cubierto en tests/api/test_terminal_local_token.py::TestRegisterLocalToken:
test_agent_deposits_token_encrypted: verifica que el token se guarda cifrado.test_register_requires_agent_jwt: sin token → 401.test_register_refresh_token_rejected: refresh token no autentica → 401.
Fase 1 — Inerte
Este endpoint no es llamado por ningún cliente hasta:
- Fase 2: el frontend web lo usa para obtener el token y pasarlo al Agent.
- Fase 3: el Agent
.exegenera el token local y lo deposita aquí.
Véase también
- [[decision—20260610—terminal-auth-local-cross-origin]]
- [[entity—terminal—endpoint—get-local-token]]
- [[entity—terminal—model—agentinstance]]
- [[concept—security—credential-encryption]]
- [[feature—terminal—auth-local-agent-fase1]]