Aller au contenu principal

Sécurité

Signature ou jeton​

Les deux se règlent côté service, indépendamment l'un de l'autre, et peuvent être exigés ensemble : une livraison doit alors satisfaire les deux. Sans l'un ni l'autre, le récepteur accepte toute livraison.

  • La signature (secret) : le service envoie dans x-lfp-signature-256 une empreinte HMAC SHA-256 du corps. Le secret ne circule jamais, et la signature atteste que le corps n'a pas été modifié en route. C'est le choix à faire dès que la livraison traverse le réseau.
  • Le jeton (token) : attendu dans Authorization: Bearer <token>. Plus simple, mais il voyage à chaque requête : à réserver à une liaison locale ou chiffrée.
createReceiver({
secret: process.env.LFP_WEBHOOKS_SECRET,
token: process.env.LFP_WEBHOOKS_TOKEN,
})

Les deux comparaisons se font en temps constant. Une livraison refusée reçoit 401, un corps illisible 400.

La signature porte sur les octets reçus : le corps doit arriver brut jusqu'au récepteur, jamais analysé puis re-sérialisé (voir Express et Fastify).

Répondre avant de traiter​

Le service abandonne une livraison au bout de dix secondes et la retente. Le récepteur répond donc 200 dès que la livraison est vérifiée, sans attendre vos gestionnaires, qui s'exécutent ensuite.

Doublons​

Une livraison retentée garde le même identifiant x-lfp-delivery. Le récepteur retient les 512 derniers (dedupeSize) : une livraison déjà vue reçoit un accusé de réception, sans que les gestionnaires ne soient rappelés. dedupeSize: 0 désactive ce filtre.

Cette mémoire vit dans le processus : après un redémarrage, ou avec plusieurs instances derrière un répartiteur, utilisez context.id comme clé d'idempotence dans votre propre stockage si un doublon pose problème.

Ordre​

Les gestionnaires s'exécutent en file, un après l'autre et dans l'ordre d'arrivée des livraisons, même quand ils sont asynchrones. Une erreur dans un gestionnaire est transmise à onError et n'interrompt pas la file.

createReceiver({
secret: process.env.LFP_WEBHOOKS_SECRET,
onError: (error, context) => logger.error({ error, event: context.event, id: context.id }),
})