Class: GeneratorLabs::Webhook

Inherits:
Object
  • Object
show all
Defined in:
lib/generatorlabs/webhook.rb

Overview

Webhook signature verification utility.

Verifies that incoming webhook requests were sent by Generator Labs using HMAC-SHA256 signatures.

Examples:

payload = GeneratorLabs::Webhook.verify(body, header, signing_secret)

Constant Summary collapse

DEFAULT_TOLERANCE =

Default tolerance in seconds for timestamp validation (5 minutes).

300

Class Method Summary collapse

Class Method Details

.verify(body, header, secret, tolerance = DEFAULT_TOLERANCE) ⇒ Hash

Verify a webhook signature and return the decoded payload.

Parameters:

  • body (String)

    The raw request body string

  • header (String)

    The X-Webhook-Signature header value

  • secret (String)

    Your webhook’s signing secret

  • tolerance (Integer) (defaults to: DEFAULT_TOLERANCE)

    Maximum age in seconds (0 to disable, default: 300)

Returns:

  • (Hash)

    The decoded JSON payload

Raises:

  • (Error)

    if verification fails



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/generatorlabs/webhook.rb', line 35

def self.verify(body, header, secret, tolerance = DEFAULT_TOLERANCE)
  raise Error, 'Missing X-Webhook-Signature header.' if header.nil? || header.empty?

  # Parse the header: t=timestamp,v1=signature
  parts = {}
  header.split(',').each do |part|
    key, value = part.split('=', 2)
    parts[key] = value
  end

  raise Error, 'Invalid X-Webhook-Signature header format.' unless parts.key?('t') && parts.key?('v1')

  # Check timestamp tolerance
  if tolerance.positive? && (Time.now.to_i - parts['t'].to_i).abs > tolerance
    raise Error, 'Webhook timestamp is outside the tolerance window.'
  end

  # Compute and compare the signature
  expected = OpenSSL::HMAC.hexdigest('sha256', secret, "#{parts['t']}.#{body}")

  raise Error, 'Webhook signature verification failed.' unless secure_compare(expected, parts['v1'])

  # Decode and return the payload
  JSON.parse(body)
end