Twilio.Webhook (twilio v0.2.0)

Copy Markdown

Checks that a webhook request really comes from Twilio.

Twilio signs every request it sends to your app (incoming messages and calls, status callbacks) with your auth token, in the X-Twilio-Signature header. Check it before acting on the request:

# A Phoenix controller
def incoming_sms(conn, params) do
  signature = conn |> get_req_header("x-twilio-signature") |> List.first()

  if Twilio.Webhook.valid?(current_url(conn), params, signature) do
    # ...
  else
    send_resp(conn, 403, "")
  end
end

The URL must be the one Twilio requested, query string included. Behind a proxy or load balancer that is the public URL (https://example.com/sms), not the one your app sees (http://10.0.0.5:4000/sms); build it from your configured host if they differ.

Requests with a JSON body (some newer Twilio products) are signed over the URL alone, with the body's SHA-256 in a bodySHA256 query parameter: pass the raw body, as a binary, instead of the params.

Webhooks signed with a SharedKey instead of the auth token say so in an X-Twilio-Webhooks-Signature-Settings header; pass it as :settings and the SharedKey secret as :auth_token.

Same rules as Twilio's official SDKs: parameters sorted by name, then value, duplicates removed; the URL tried with and without its default port, since Twilio signs either; signatures compared in constant time.

Summary

Functions

The signature Twilio would send for a request to url with params: useful in your tests, to sign the requests you send to your own webhook.

Whether signature (the X-Twilio-Signature header) is Twilio's signature of a request to url with params.

Functions

signature(url, params, opts \\ [])

@spec signature(String.t(), map() | keyword() | [{String.t(), term()}], keyword()) ::
  String.t()

The signature Twilio would send for a request to url with params: useful in your tests, to sign the requests you send to your own webhook.

Takes the :auth_token and :algorithm options of valid?/4.

valid?(url, params, signature, opts \\ [])

@spec valid?(
  String.t(),
  map() | keyword() | [{String.t(), term()}] | binary(),
  String.t() | nil,
  keyword()
) :: boolean()

Whether signature (the X-Twilio-Signature header) is Twilio's signature of a request to url with params.

params is the form body as a map, keyword list or list of pairs (repeated names allowed), or the raw body as a binary for JSON requests.

Options:

  • :auth_token: the secret it was signed with. Default: config :twilio, auth_token: ..., then the TWILIO_AUTH_TOKEN environment variable
  • :settings: the X-Twilio-Webhooks-Signature-Settings header, when the request has one
  • :algorithm: :hmac_sha1 (the default) or :hmac_sha256, instead of reading it from :settings