Complete implementation ready for Coolify: - Node.js 22 + Fastify + socket.io backend - PostgreSQL 16 + Redis 7 services - Docker Compose configuration - Deployment scripts and documentation Co-Authored-By: Paperclip <noreply@paperclip.ing>
4 KiB
4 KiB
AgentHub API J3 — Auth REST
Vue d'ensemble
Authentification en deux niveaux :
- API Token long-lived (
ah_live_XXXX_secret) — stocké hashé argon2id, émis une seule fois - 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:
{
"name": "agent-name",
"displayName": "Agent Display Name",
"role": "agent" | "admin"
}
Response (201):
{
"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 charsrole:admin|agent
Audit: agent-created
GET /api/v1/agents
Lister tous les agents (admin).
Response (200):
[
{
"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:
{
"scopes": { "read": true, "write": true },
"expiresAt": "2025-12-31T23:59:59Z" // optional
}
Response (201):
{
"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
secretest 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éussie404: Token non trouvé400: Token déjà révoqué
Effet:
status→revokedrevokedAt→ 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:
{
"apiToken": "ah_live_XXXX_secret"
}
Response (200):
{
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 900,
"agentId": "uuid",
"agentName": "agent-name",
"agentRole": "agent"
}
JWT Payload:
{
"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.
# 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/<agent-id>/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/<token-id>
# → 204 No Content
Rotation de tokens
Pour la rotation sans interruption :
- Émettre un nouveau token (
POST /agents/:id/tokens) - Période de chevauchement (24h recommandé) : les deux tokens sont valides
- Révoquer l'ancien token (
DELETE /tokens/:old-id) - Le nouveau token continue de fonctionner
Audit
Tous les événements d'authentification sont loggés dans audit_events :
agent-created: création d'agenttoken-issued: émission de tokentoken-revoked: révocation de tokenjwt-issued: échange token → JWT
Le payload_hash (sha256) est stocké, jamais le secret en clair.