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()
end
end
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() ... ...