Webhook Email inbound dan HMAC signature
Webhook Email inbound
Halaman ini mencakup dua topik yang berkaitan:
- Bagaimana
webhook-ingestormenerima email inbound dari SendGrid Inbound Parse atau relay kustom. - Referensi lengkap algoritma HMAC-SHA256 yang dipakai untuk memvalidasi webhook Meta (WhatsApp/Instagram/Messenger) dan email.
Endpoint
| Item | Nilai |
|---|---|
| Metode | POST |
| Path | /webhook/email |
| Host | webhook-ingestor di WEBHOOK_INGESTOR_PORT (default 3001) |
| Auth | Header X-Email-Webhook-Signature (opsional — dilewati jika EMAIL_WEBHOOK_SECRET kosong) |
| Content-Type | application/json |
Tidak ada handshake verifikasi seperti Meta — SendGrid dan relay lain tidak memakai challenge/subscribe. Ingestor hanya menerima POST.
Format payload
Ingestor menerima JSON SendGrid-compatible. Field minimum yang wajib ada: from. Tanpa from, request ditolak 400 PayloadError.
Code
Implementasi ada di crates/webhook-ingestor/src/routes.rs::receive_email_webhook.
Alur pemrosesan
- Verifikasi signature (opsional) — jika
EMAIL_WEBHOOK_SECRETdi.envterisi, ingestor memvalidasi headerX-Email-Webhook-Signaturedengan HMAC-SHA256. Jika secret kosong, verifikasi dilewati dan semua request diterima. - Parse JSON — body diuraikan menjadi
serde_json::Value. Kegagalan parse menghasilkan400 PayloadError. - Validasi minimum — field
fromwajib ada; tanpa itu400. - Bangun event — dikemas sebagai
InboundRawEventdenganchannel: "email"danintegration_account_id: None(email tidak memakai lookup akun di ingestor; chat-engine yang menentukan route). - Produksi ke Kafka — topik
channel.inbound.raw, keyevent_idUUID. - Audit — tulis fire-and-forget ke MongoDB
webhook_auditdengansource: "email".
Code
Konsumsi oleh chat-engine-email
chat-engine-email berlangganan channel.inbound.raw dengan filter channel == "email" dan:
- Mengekstrak
from,to,subject,text/htmldari payload. - Mencari integration account berdasarkan alamat
toyang cocok denganconfig->>'inbox_address'. - Upsert kontak di
contactsberdasarkan email pengirim. - Threading conversation berdasarkan header
In-Reply-To/Referencesbila ada, jika tidak membuatconversationsbaru. - Simpan pesan ke MongoDB
messages. - Publikasi
ConversationUpdateEventke Redis.
Parser ada di crates/chat-engine/src/parser/email.rs (fungsi parse_email_message).
HMAC signature untuk Meta dan Email
Bagian ini adalah referensi algoritma yang dipakai oleh kedua verifikasi signature. Kodenya ada di crates/webhook-ingestor/src/signature.rs.
Algoritma
Code
- Key — untuk Meta:
META_APP_SECRET. Untuk email:EMAIL_WEBHOOK_SECRET. - Message — raw bytes body sebelum parsing JSON. Jangan memformat ulang atau mem-prettify — satu byte berubah, signature invalid.
- Digest — SHA-256 (256 bit / 32 byte).
- Encoding — hex lowercase.
Format header
| Channel | Header | Nilai |
|---|---|---|
X-Hub-Signature-256 | sha256=<hex> (prefix sha256= wajib) | |
X-Hub-Signature-256 | sha256=<hex> | |
| Messenger | X-Hub-Signature-256 | sha256=<hex> |
X-Email-Webhook-Signature | <hex> (tanpa prefix) |
Perbedaan prefix adalah konvensi Meta vs format kustom kami. Fungsi verify_signature di signature.rs menangani prefix strip otomatis untuk header Meta.
Verifikasi yang aman (constant-time)
Jangan membandingkan signature dengan == — vulnerable terhadap timing attack. Gunakan comparator konstan:
Code
Hmac::verify_slice adalah fungsi comparator constant-time: waktu eksekusinya tidak bergantung pada byte mana yang pertama berbeda. Ini mencegah attacker mengukur respons untuk mengkonstruksi signature valid.
Men-generate signature (untuk klien)
Jika Anda membangun relay email yang mengirim ke /webhook/email, generate signature dengan Python:
Code
Dengan Node.js:
Code
Catatan raw body: jika relay Anda mem-parse JSON dan men-serialisasi ulang sebelum mengirim, signature akan invalid. Pastikan Anda meng-HMAC byte yang sama persis dengan yang dikirim.
Troubleshooting
400 Missing required field 'from'— payload email tidak memiliki fieldfrom. SendGrid Inbound Parse selalu mengirimnya; periksa apakah relay Anda memakai format lain.401 InvalidSignaturepada webhook email —EMAIL_WEBHOOK_SECRETdi relay berbeda dengan yang di ingestor, atau raw body dimutasi oleh proxy/middleware. Gunakan Wireshark atau log akses untuk membandingkan byte.401 MissingSignaturepada webhook Meta tetapi header muncul di proxy — reverse proxy Anda menghapus headerX-Hub-Signature-256. Tambahkan:Code- Verifikasi Meta lolos tetapi email tidak — ingat prefix header berbeda. Email memakai hex tanpa prefix
sha256=. Jika relay Anda menambahkan prefix, ingestor akan menolaknya. - Email inbound tidak muncul di inbox — periksa
chat-engine-emaillogs. Biasanya karena alamattotidak cocok denganintegration_accounts.config->>'inbox_address'.
File terkait
- HTTP handler:
crates/webhook-ingestor/src/routes.rs::receive_email_webhook - Signature verification:
crates/webhook-ingestor/src/signature.rs::verify_signature,verify_email_signature - Parser chat-engine:
crates/chat-engine/src/parser/email.rs::parse_email_message - Sender:
crates/message-sender/src/email.rs(menggunakan SMTP lewatlettre)