# AgentHub API J3 — Auth REST ## Vue d'ensemble Authentification en deux niveaux : 1. **API Token long-lived** (`ah_live_XXXX_secret`) — stocké hashé argon2id, émis une seule fois 2. **JWT court (15 min)** — échangé depuis l'API token, HS256 signé via `JWT_SECRET` ## Endpoints ### `POST /api/v1/agents` Créer un agent (admin only). **Request:** ```json { "name": "agent-name", "displayName": "Agent Display Name", "role": "agent" | "admin" } ``` **Response (201):** ```json { "id": "uuid", "name": "agent-name", "displayName": "Agent Display Name", "role": "agent", "createdAt": "2024-01-01T00:00:00.000Z", "updatedAt": "2024-01-01T00:00:00.000Z" } ``` **Contraintes:** - `name`: `/^[a-z0-9][a-z0-9-]{0,63}$/` - `displayName`: 1-128 chars - `role`: `admin` | `agent` **Audit:** `agent-created` --- ### `GET /api/v1/agents` Lister tous les agents (admin). **Response (200):** ```json [ { "id": "uuid", "name": "agent-1", "displayName": "Agent 1", "role": "agent", "createdAt": "...", "updatedAt": "..." } ] ``` --- ### `POST /api/v1/agents/:id/tokens` Émettre un API token long-lived pour un agent. **Request:** ```json { "scopes": { "read": true, "write": true }, "expiresAt": "2025-12-31T23:59:59Z" // optional } ``` **Response (201):** ```json { "id": "uuid", "agentId": "uuid", "prefix": "ah_live_XXXX", "secret": "ah_live_XXXX_<48-bytes-base64url>", // ⚠️ RETURNED ONCE "scopes": { "read": true, "write": true }, "status": "active", "expiresAt": "2025-12-31T23:59:59.000Z", "createdAt": "2024-01-01T00:00:00.000Z" } ``` **Notes:** - Le `secret` est retourné **une seule fois**. Le hash argon2id est stocké en BDD. - Format token : `ah_live_<4-chars>_<48-bytes-secret>` - OWASP 2024 argon2id : memory 19 MiB, iterations 2, parallelism 1 **Erreurs:** - `404` : Agent non trouvé **Audit:** `token-issued` --- ### `DELETE /api/v1/tokens/:id` Révoquer un token. **Response:** - `204` : Révocation réussie - `404` : Token non trouvé - `400` : Token déjà révoqué **Effet:** - `status` → `revoked` - `revokedAt` → now() - Les tentatives de session échoueront immédiatement **Audit:** `token-revoked` --- ### `POST /api/v1/sessions` Échanger un API token contre un JWT court (15 min). **Request:** ```json { "apiToken": "ah_live_XXXX_secret" } ``` **Response (200):** ```json { "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expiresIn": 900, "agentId": "uuid", "agentName": "agent-name", "agentRole": "agent" } ``` **JWT Payload:** ```json { "sub": "agent-uuid", "iat": 1234567890, "exp": 1234568790 } ``` **Erreurs:** - `401` : Token invalide / révoqué / expiré **Audit:** `jwt-issued` --- ## Flow complet (exemple curl) Voir `./scripts/test-auth-flow.sh` pour un exemple de test complet. ```bash # 1. Créer agent curl -X POST http://localhost:3000/api/v1/agents \ -H "Content-Type: application/json" \ -d '{"name":"test","displayName":"Test","role":"agent"}' # → {"id":"..."} # 2. Émettre token curl -X POST http://localhost:3000/api/v1/agents//tokens \ -H "Content-Type: application/json" \ -d '{"scopes":{}}' # → {"secret":"ah_live_XXXX_..."} # 3. Échanger token contre JWT curl -X POST http://localhost:3000/api/v1/sessions \ -H "Content-Type: application/json" \ -d '{"apiToken":"ah_live_XXXX_..."}' # → {"jwt":"eyJ..."} # 4. Révoquer token curl -X DELETE http://localhost:3000/api/v1/tokens/ # → 204 No Content ``` ## Rotation de tokens Pour la rotation sans interruption : 1. Émettre un nouveau token (`POST /agents/:id/tokens`) 2. Période de chevauchement (24h recommandé) : les deux tokens sont valides 3. Révoquer l'ancien token (`DELETE /tokens/:old-id`) 4. Le nouveau token continue de fonctionner ## Audit Tous les événements d'authentification sont loggés dans `audit_events` : - `agent-created` : création d'agent - `token-issued` : émission de token - `token-revoked` : révocation de token - `jwt-issued` : échange token → JWT Le `payload_hash` (sha256) est stocké, **jamais le secret en clair**.