Mailglass.Webhook.Providers.SendGrid (Mailglass v1.0.0)

Copy Markdown View Source

SendGrid Event Webhook verifier + normalizer.

Verifier: ECDSA P-256 (prime256v1 / secp256r1) signature over timestamp <> raw_body per the SendGrid Event Webhook security docs. Public key is supplied as base64-encoded SubjectPublicKeyInfo DER (NOT PEM — Pitfall 1; the SendGrid dashboard ships DER without -----BEGIN PUBLIC KEY----- framing).

The verifier pattern-matches strictly on true from :public_key.verify/4. false, {:error, _}, and DER-decode exceptions all collapse to %SignatureError{type: :bad_signature} per CONTEXT D-03 (closes the "wrong algo silently returns false" footgun).

Replay protection: 300-second timestamp tolerance window (Stripe / Svix / Standard Webhooks consensus; SendGrid does not document one). Configurable via config :mailglass, :sendgrid, timestamp_tolerance_seconds: N.

Provider identity lives in Event.metadata

The %Mailglass.Events.Event{} struct has no :provider column (per V02 schema — provider identity lives on mailglass_webhook_events). This module stashes "event" + "sg_message_id" in Event.metadata with STRING keys (revision W9; JSONB roundtrip safety). Plan 06's Ingest Multi reads these metadata keys to populate the mailglass_webhook_events row's UNIQUE columns.

Normalizer: decodes the JSON array of events (1..128 per request); maps each event string to the Anymail taxonomy verbatim. Unmapped strings fall through to :unknown with Logger.warning per D-05.