CreaRack-SL

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> usando get_agent_from_request().
  • El agent_id se 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

  1. 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., o None si es inválido.
  2. 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.
  3. Salvaguarda en base de datos: agent.local_token_enc = ... + agent.save(update_fields=[...]).

    • Actualiza solo local_token_enc y updated_at (efficient update).
  4. Logging: logger.info(f"Local token registered for agent {agent_id}").

    • Auditoría sin exponer el token.

Flujo

  1. Agent genera un token local (UUID o JWT firmado localmente con su keypair).
  2. Agent obtiene su access token JWT (via POST /api/agent/auth o mantiene uno en sesión).
  3. Agent envía POST /api/agent/register-local-token con:
    • Header: Authorization: Bearer <access_token>
    • Body: {"local_token": "<opaco>"}
  4. Servidor extrae agent_id del JWT.
  5. Servidor busca AgentInstance(agent_id=...).
  6. Servidor cifra el token y lo guarda en local_token_enc.
  7. Servidor responde 200 OK.

Seguridad

  • No almacenamiento en claro: Fernet (simétricamente cifrado con CREDENTIAL_ENCRYPTION_KEY).
  • Prevención de spoofing: agent_id extraí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 .exe genera 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]]