Skip to content

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_credentials grant (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 :

RessourceUsage du tenantHash
Cache RedisInclus dans la cle : opcovia:cache:{opcoId}:{tenantHash}:...
Jobs BullMQChamp tenantHash dans chaque job
audit_logsColonne tenant_hash
job_resultsColonne tenant_hash
webhooksColonne tenant_hash — un CFA ne voit que ses webhooks
webhook_deliveriesFiltrees par webhook (lie au tenant)
snapshotsColonne tenant_hash
sync_jobsColonne 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 traitement
  • sync_jobs.api_key_encrypted — dechiffree pour chaque batch sync
  • poller_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  ← purge

Les 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 :

typescript
// 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, *.internal

De 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 :

json
{
  "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 ​

SecretStockageUsage
MASTER_KEYEnv varChiffrement AES-256-GCM des API keys et fichiers en DB
OPCO_CLIENT_ID / OPCO_CLIENT_SECRETEnv varOAuth2 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_SECRETEnv var hubSignature HMAC des JWT de grant
HUB_ADMIN_KEYEnv var hubProtection API admin
Hub API keysFichier YAMLAuthentification WebSocket des instances
Webhook secretsDB (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