TreasuryPrime.WebhookSignature (TreasuryPrime v1.0.0)

Copy Markdown View Source

Validates that an incoming webhook request genuinely originated from Treasury Prime.

Treasury Prime doesn't use HMAC request signing. Instead, validation is configured per-TreasuryPrime.Webhook object via a basic_user / basic_secret pair you choose when creating the webhook (see TreasuryPrime.Webhook.create/3). When set, every notification sent to that webhook's URL includes an Authorization header equal to "Basic " <> Base.encode64("#{basic_user}:#{basic_secret}") — i.e. the exact same shape as the header you send when authenticating your requests to Treasury Prime, just compared in the other direction.

Usage

defmodule MyAppWeb.TreasuryPrimeWebhookController do
  use MyAppWeb, :controller

  @basic_user "myapp"
  @basic_secret System.fetch_env!("TREASURY_PRIME_WEBHOOK_SECRET")

  def create(conn, params) do
    header = conn |> Plug.Conn.get_req_header("authorization") |> List.first()

    if TreasuryPrime.WebhookSignature.valid?(header, @basic_user, @basic_secret) do
      event = TreasuryPrime.WebhookEvent.parse!(params)
      MyApp.Webhooks.handle(event)
      send_resp(conn, 200, "")
    else
      send_resp(conn, 401, "")
    end
  end
end

Comparison is done in constant time to avoid leaking information about the expected secret through response-time side channels.

Summary

Functions

Returns true if authorization_header (the raw value of the incoming request's Authorization header, with or without a leading "Basic ") matches the expected value for the given basic_user/basic_secret pair configured on the TreasuryPrime.Webhook object.

Functions

valid?(authorization_header, basic_user, basic_secret)

@spec valid?(String.t() | nil, String.t(), String.t()) :: boolean()

Returns true if authorization_header (the raw value of the incoming request's Authorization header, with or without a leading "Basic ") matches the expected value for the given basic_user/basic_secret pair configured on the TreasuryPrime.Webhook object.

Returns false (never raises) for nil, malformed, or mismatched input — safe to call directly on a possibly-missing header.