Module: LetMeSendEmail::Webhooks

Defined in:
lib/letmesendemail/webhooks.rb

Overview

Verifies signatures on raw letmesend.email webhook requests.

Constant Summary collapse

REQUIRED_HEADERS =
%w[webhook-id webhook-log-id webhook-timestamp webhook-signature].freeze
DEFAULT_TOLERANCE =
300

Class Method Summary collapse

Class Method Details

.resolve_headers(headers) ⇒ Hash

Normalizes Rack/CGI and ordinary HTTP header hashes.

Parameters:

  • headers (Hash)

Returns:

  • (Hash)


34
35
36
37
38
39
# File 'lib/letmesendemail/webhooks.rb', line 34

def resolve_headers(headers)
  headers.each_with_object({}) do |(key, value), result|
    normalized = key.to_s.downcase.sub(/\Ahttp_/, '').tr('_', '-')
    result[normalized] = value.to_s
  end
end

.verify(payload, headers, secret, tolerance: DEFAULT_TOLERANCE) ⇒ Hash

Verifies a webhook signature before parsing the JSON payload.

Parameters:

  • payload (String)

    exact raw request body

  • headers (Hash)

    request headers

  • secret (String)

    Base64 secret, optionally prefixed with whsec_

  • tolerance (Integer) (defaults to: DEFAULT_TOLERANCE)

    allowed timestamp skew in seconds

Returns:

  • (Hash)

    verified JSON object

Raises:



22
23
24
25
26
27
28
29
# File 'lib/letmesendemail/webhooks.rb', line 22

def verify(payload, headers, secret, tolerance: DEFAULT_TOLERANCE)
  validate_inputs!(payload, headers, secret, tolerance)
  extracted = extract_headers(headers)
  validate_timestamp!(extracted['webhook-timestamp'], tolerance)
  expected = expected_signature(payload, extracted, secret)
  validate_signature!(extracted['webhook-signature'], expected)
  parse_payload(payload)
end