Securite
Authentification a 2 niveaux
L'API Convergence utilise deux niveaux d'authentification :
┌──────────────┐ ┌────────────────┐ ┌──────────────┐
│ Client CFA │ │ Opcovia │ │ API OPCO │
│ │ │ │ │ │
│ X-API-KEY ──────────► │ X-API-KEY ─────────────► │ │
│ │ │ │ │ │
│ │ │ Bearer token ─────────► │ │
│ │ │ (OAuth2, gere │ │ │
│ │ │ cote serveur) │ │ │
└──────────────┘ └────────────────┘ └──────────────┘Niveau 1 : Bearer Token (OAuth2 client_credentials)
- Identifie l'editeur (Eduvia) aupres de l'OPCO
- Obtenu via OAuth2
client_credentialsgrant (client_id + client_secret) - Gere cote serveur par le
TokenManager: le CFA ne le voit jamais - Refresh automatique avant expiration
- Stocke en memoire uniquement (pas en DB, pas en Redis)
Niveau 2 : X-API-KEY (par CFA)
- Identifie le CFA aupres de l'OPCO
- Fourni par le CFA dans chaque requete HTTP
- Forwarde tel quel a l'API OPCO
- Chaque CFA a sa propre cle, attribuee par l'OPCO
Le CFA ne fournit que son X-API-KEY. Opcovia ajoute automatiquement le Bearer token.
Flux d'authentification complet
Isolation tenant (tenantHash)
Toute l'isolation multi-tenant repose sur un hash de l'API key :
tenantHash = SHA-256(X-API-KEY)Ce hash est utilise comme cle de partition dans :
| Ressource | Usage du tenantHash |
|---|---|
| Cache Redis | Inclus dans la cle : opcovia:cache:{opcoId}:{tenantHash}:... |
| Jobs BullMQ | Champ tenantHash dans chaque job |
audit_logs | Colonne tenant_hash |
job_results | Colonne tenant_hash |
webhooks | Colonne tenant_hash — un CFA ne voit que ses webhooks |
webhook_deliveries | Filtrees par webhook (lie au tenant) |
snapshots | Colonne tenant_hash |
sync_jobs | Colonne tenant_hash |
Un CFA ne peut jamais acceder aux donnees d'un autre CFA.
Chiffrement des donnees sensibles
API keys en base de donnees
Les X-API-KEY sont chiffrees avec AES-256-GCM avant stockage :
Format : iv:tag:ciphertext (hex)
Cle de chiffrement : MASTER_KEY (env var, 32 bytes hex)
Algorithme : AES-256-GCM
IV : 12 bytes aleatoires par valeur
Tag : authentification integrite (GCM)Tables concernees :
job_results.api_key_encrypted— dechiffree par le worker au traitementsync_jobs.api_key_encrypted— dechiffree pour chaque batch syncpoller_registrations.api_key_encrypted— dechiffree pour chaque poll
Fichiers en base de donnees
Les fichiers PDF (factures, conventions, certificats) sont chiffres AES-256-GCM en DB (job_results.files_encrypted) et purges apres traitement :
POST /factures (multipart)
→ Producer : chiffre fichier → INSERT en DB
→ Redis : payload SANS fichier (trop volumineux)
→ Worker : dechiffre fichier depuis DB
→ Pipeline : envoie a l'OPCO
→ Worker : UPDATE files_encrypted = NULL ← purgeLes fichiers ne transitent jamais par Redis et ne restent en DB que le temps du traitement.
Donnees sensibles dans les logs
Le sanitizer redacte automatiquement les champs sensibles dans les logs et les payloads d'audit :
- API keys masquees
- Donnees personnelles redactees dans les logs
Protection SSRF (webhooks)
Les URL de callback des webhooks sont validees contre les attaques SSRF :
// Regle : HTTPS obligatoire (sauf dev)
// Interdit :
// - localhost, 127.0.0.1, ::1
// - 10.x.x.x
// - 192.168.x.x
// - 172.16-31.x.x
// - *.local, *.internalDe plus, les redirections HTTP sont bloquees (redirect: 'manual').
Hub : authentification JWT
Connexion WebSocket
Les instances Opcovia s'authentifient aupres du hub via une API key passee en query param :
ws://hub.opcovia.dev:4000/ws?apiKey=<hub-api-key>Les API keys sont stockees dans un fichier YAML (api-keys.yaml) et gerees par le KeyStore. Chaque cle est associee a un tenantId et un name.
JWT de grant
Chaque grant inclut un JWT signe HMAC-SHA256 :
{
"opcoId": "opco-ep",
"method": "GET",
"route": "/dossiers",
"instanceId": "inst-42",
"iat": 1711728000,
"exp": 1711728030,
"iss": "opcovia-hub"
}Ce JWT est envoye dans le header Opcovia-Hub-Token lors de l'appel OPCO. Il prouve que l'appel a ete autorise par le hub et permet le tracing.
API Admin
L'API admin du hub est protegee par un HUB_ADMIN_KEY distinct :
Authorization: Bearer <admin-key>Routes admin : gestion des API keys, listing des instances, rechargement de config.
Resume des secrets
| Secret | Stockage | Usage |
|---|---|---|
MASTER_KEY | Env var | Chiffrement AES-256-GCM des API keys et fichiers en DB |
OPCO_CLIENT_ID / OPCO_CLIENT_SECRET | Env var | OAuth2 client_credentials pour obtenir le Bearer token |
X-API-KEY (CFA) | Chiffre en DB (AES-256-GCM) | Identifie le CFA aupres de l'OPCO |
HUB_JWT_SECRET | Env var hub | Signature HMAC des JWT de grant |
HUB_ADMIN_KEY | Env var hub | Protection API admin |
| Hub API keys | Fichier YAML | Authentification WebSocket des instances |
| Webhook secrets | DB (plain, generes) | Signature HMAC-SHA256 des payloads webhook |
Bonnes pratiques appliquees
- Secrets jamais dans Redis : les API keys et fichiers ne transitent que par PostgreSQL (chiffres)
- Purge apres traitement : les fichiers sont supprimes de la DB des que le worker a termine
- Pas de PII dans les logs : le sanitizer redacte automatiquement
- Pas de redirections HTTP : les webhooks utilisent
redirect: 'manual' - Timeouts partout : 30s pour les appels OPCO, 10s pour les webhooks, TTL sur les slots hub