Unicom360.Webhook.Plug (Unicom360 v0.1.0)

Copy Markdown View Source

A plug that receives Unicom360 webhooks: it verifies the signature, parses the event and calls your handler.

# In a Phoenix router (or a Plug.Router):
forward "/webhooks/unicom360/whatsapp", Unicom360.Webhook.Plug,
  secret: {MyApp.Config, :unicom360_webhook_secret, []},
  handler: MyApp.WhatsAppEvents

Options:

  • :secret (required) - the 43-character string from the portal, or {module, function, args} called on each request. Prefer the tuple, so the secret is not compiled into your router. A value that is nil, empty or only whitespace makes init/1 raise Unicom360.Error (:configuration_error); a tuple that returns one answers 500 (below).
  • :handler (required) - a module implementing Unicom360.Webhook.Handler.
  • :tolerance - the largest clock skew accepted, in milliseconds. Default 300_000.

It answers:

  • 200 when the handler returns :ok;
  • 401 with the failure's reason as the body, when verification fails;
  • 400 when the body cannot be read, or a verified body is not a JSON object with an event;
  • 413 when the body is over 1 MiB (only when this plug reads the body itself);
  • 500 when the handler returns anything else;
  • 500, without reading the body or calling the handler, and with a log line, when an {m, f, a} secret returns nil, an empty string or only whitespace: anyone can forge a signature with an empty key, and it is a fault in your configuration, not a failed verification.

The raw body

Signatures cover the exact bytes received. Mount this plug ahead of Plug.Parsers and it reads the body itself. Behind Plug.Parsers, add Unicom360.Webhook.BodyReader as the parsers' :body_reader, and this plug uses conn.assigns.raw_body.