View Source WorkOS.Webhooks (WorkOS SDK for Elixir v1.1.0)
Manage timestamp and signature validation of Webhooks in WorkOS.
Summary
Functions
Verify webhook payload and return an event.
Functions
@spec construct_event( payload :: String.t(), sigHeader :: String.t(), secret :: String.t(), tolerance :: pos_integer() ) :: {:ok, payload :: map()} | {:error, message :: String.t()}
Verify webhook payload and return an event.
payload is the raw, unparsed content body sent by WorkOS, which can be
retrieved with Plug.Conn.read_body/2, Note that Plug.Parsers will read
and discard the body, so you must implement a custom body reader if the
plug is located earlier in the pipeline.
sigHeader is the value of workos-signature header, which can be fetched
with Plug.Conn.get_req_header/2, i.e. Plug.Conn.get_req_header(conn, "workos-signature").
secret is your webhook endpoint's secret from the WorkOS Dashboard.
tolerance is the allowed deviation in seconds from the current system time
to the timestamp found in signature. Defaults to 180 seconds (3 minutes).
WorkOS API reference: https://workos.com/docs/webhooks/securing-your-webhook-endpoint/validating-events-are-from-workos
Example plug in your consuming application which places the constructed
event in the conn assigns
defmodule MyAppWeb.WorkOSWebhooksPlug do @behaviour Plug
alias Plug.Conn
def init(config), do: config
# Match on any requests from workos, i.e. the Endpoint URL configured in # the WorkOS Dashboard, adjust @requestpath as appropriate @request_path "/webhooks/workos" def call(%{request_path: @request_path} = conn, ) do
signing_secret = Application.get_env(:workos, :webhook_signing_secret)
[worksos_signature] = Conn.get_req_header(conn, "workos-signature")
with {:ok, body, _} <- Conn.read_body(conn),
{:ok, workos_event} <-
WorkOS.Webhooks.construct_event(body, worksos_signature, signing_secret) do
Conn.assign(conn, :workos_event, workos_event)
else
{:error, error} ->
conn
|> Conn.send_resp(:bad_request, error.message)
|> Conn.halt()
endend
def call(conn, _), do: conn end
As per the aforementioned note about Plug.Parsers the above plug would need to
precede these in Endpoint.ex ... i.e.
defmodule MyAppWeb.Endpoint do ... ...
plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
plug MyAppWeb.WorkOSWebhooksPlug
plug Plug.Parsers, parsers: [:urlencoded, :multipart, :json], pass: ["/"], json_decoder: Phoenix.json_library() ... ...