Module: GlobiGuard::TrustWebhook

Defined in:
lib/globiguard.rb

Class Method Summary collapse

Class Method Details

.secure_compare(left, right) ⇒ Object



177
178
179
180
# File 'lib/globiguard.rb', line 177

def secure_compare(left, right)
  return false unless left.bytesize == right.bytesize
  left.bytes.zip(right.bytes).reduce(0) { |memo, pair| memo | (pair[0] ^ pair[1]) }.zero?
end

.verify(headers, raw_body, signing_secret, tolerance_seconds: 300) ⇒ Object



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/globiguard.rb', line 161

def verify(headers, raw_body, signing_secret, tolerance_seconds: 300)
  normalized = headers.transform_keys { |key| key.to_s.downcase }
  delivery = normalized["x-globiguard-delivery-id"]
  timestamp = normalized["x-globiguard-timestamp"]
  event_type = normalized["x-globiguard-event-type"]
  signature = normalized["x-globiguard-signature"]
  return { ok: false, error: "Missing required webhook headers." } unless delivery && timestamp && event_type && signature
  return { ok: false, error: "Webhook timestamp is outside the replay window." } if (Time.now.to_i - timestamp.to_i).abs > tolerance_seconds

  signed = "globiguard-hmac-sha256-v1.#{delivery}.#{timestamp}.#{event_type}.#{raw_body}"
  expected = "v1=#{OpenSSL::HMAC.hexdigest("SHA256", signing_secret, signed)}"
  return { ok: false, error: "Invalid webhook signature." } unless secure_compare(expected, signature)

  { ok: true, envelope: JSON.parse(raw_body) }
end