The bytes a webhook was sent, kept for its signature check.
A webhook is signed over its body exactly as it was sent — Svix (Resend), Stripe and GitHub all
sign that way — and Plug.Parsers reads the body to decode it, so a route behind the endpoint
sees only the decoded params. One decoded and encoded again is a different text and never matches.
Give the endpoint's parsers this body reader:
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Phoenix.json_library(),
body_reader: {MishkaCmsCore.Runtime.Plugs.RawBody, :read_body, []}A request whose path starts with /webhooks/ then keeps its body in conn.private[:raw_body]
(raw_body/1) beside the params, and every other request is read as Plug reads it and keeps
nothing. An extension mounts its webhooks under that prefix — /webhooks/<extension>/<sender> —
through MishkaCmsCore.ExtensionRouter's :api routes.
The parsers' own :length limit applies, so a body is never kept past what they would read. A
request whose content type no parser reads is not read at all, and keeps nothing: its route reads
it with Plug.Conn.read_body/2.
Summary
Functions
The path prefix whose requests keep their body.
The body a webhook request was sent, or nil when it was not kept.
Plug.Conn.read_body/2, keeping what it reads when the request is a webhook's. For
Plug.Parsers' :body_reader.
Functions
@spec prefix() :: String.t()
The path prefix whose requests keep their body.
@spec raw_body(Plug.Conn.t()) :: binary() | nil
The body a webhook request was sent, or nil when it was not kept.
@spec read_body(Plug.Conn.t(), keyword()) :: {:ok, binary(), Plug.Conn.t()} | {:more, binary(), Plug.Conn.t()} | {:error, term()}
Plug.Conn.read_body/2, keeping what it reads when the request is a webhook's. For
Plug.Parsers' :body_reader.