Module: StraddlePay::Webhook::Signature

Defined in:
lib/straddle_pay/webhook.rb

Overview

Low-level signature verification following the Svix protocol.

Constant Summary collapse

HEADER_PREFIXES =
%w[svix webhook].freeze

Class Method Summary collapse

Class Method Details

.compute_signature(msg_id, timestamp, payload, secret) ⇒ String

Computes the expected HMAC-SHA256 signature for a webhook payload.

Parameters:

  • msg_id (String)

    the message ID

  • timestamp (String)

    Unix timestamp

  • payload (String)

    raw request body

  • secret (String)

    webhook signing secret

Returns:

  • (String)

    Base64-encoded signature



64
65
66
67
68
69
# File 'lib/straddle_pay/webhook.rb', line 64

def compute_signature(msg_id, timestamp, payload, secret)
  key = decode_secret(secret)
  signed_content = "#{msg_id}.#{timestamp}.#{payload}"
  digest = OpenSSL::HMAC.digest("SHA256", key, signed_content)
  [digest].pack("m0")
end

.generate_header(msg_id:, timestamp:, payload:, secret:) ⇒ Hash

Generates Svix-compatible headers for testing.

Parameters:

  • msg_id (String)

    message ID

  • timestamp (String, Integer)

    Unix timestamp

  • payload (String)

    raw request body

  • secret (String)

    webhook signing secret

Returns:

  • (Hash)

    headers hash with svix-id, svix-timestamp, svix-signature



78
79
80
81
82
83
84
85
86
# File 'lib/straddle_pay/webhook.rb', line 78

def generate_header(msg_id:, timestamp:, payload:, secret:)
  ts = timestamp.to_s
  sig = compute_signature(msg_id, ts, payload, secret)
  {
    "svix-id" => msg_id,
    "svix-timestamp" => ts,
    "svix-signature" => "v1,#{sig}"
  }
end

.verify_header(payload, headers, secret, tolerance: nil) ⇒ true

Verifies the webhook signature header against the payload.

Parameters:

  • payload (String)

    raw request body

  • headers (Hash, #[])

    request headers

  • secret (String)

    webhook signing secret

  • tolerance (Integer, nil) (defaults to: nil)

    max age in seconds (nil to skip time check)

Returns:

  • (true)

Raises:



50
51
52
53
54
55
# File 'lib/straddle_pay/webhook.rb', line 50

def verify_header(payload, headers, secret, tolerance: nil)
  msg_id, timestamp, signature = extract_headers(headers)
  verify_timestamp(timestamp, tolerance) if tolerance
  expected = compute_signature(msg_id, timestamp, payload, secret)
  verify_signature(expected, signature)
end