Module: Schematic::Webhooks

Defined in:
lib/schematic/webhooks/client.rb,
lib/schematic/webhook_verification.rb,
lib/schematic/webhooks/types/get_webhook_response.rb,
lib/schematic/webhooks/types/list_webhooks_params.rb,
lib/schematic/webhooks/types/count_webhooks_params.rb,
lib/schematic/webhooks/types/list_webhooks_request.rb,
lib/schematic/webhooks/types/count_webhooks_request.rb,
lib/schematic/webhooks/types/list_webhooks_response.rb,
lib/schematic/webhooks/types/count_webhooks_response.rb,
lib/schematic/webhooks/types/create_webhook_response.rb,
lib/schematic/webhooks/types/delete_webhook_response.rb,
lib/schematic/webhooks/types/update_webhook_response.rb,
lib/schematic/webhooks/types/get_webhook_event_response.rb,
lib/schematic/webhooks/types/list_webhook_events_params.rb,
lib/schematic/webhooks/types/count_webhook_events_params.rb,
lib/schematic/webhooks/types/create_webhook_request_body.rb,
lib/schematic/webhooks/types/list_webhook_events_request.rb,
lib/schematic/webhooks/types/update_webhook_request_body.rb,
lib/schematic/webhooks/types/count_webhook_events_request.rb,
lib/schematic/webhooks/types/list_webhook_events_response.rb,
lib/schematic/webhooks/types/count_webhook_events_response.rb

Defined Under Namespace

Modules: Types Classes: Client, InvalidSignatureError, MissingSignatureError, MissingTimestampError, WebhookSignatureError

Constant Summary collapse

SIGNATURE_HEADER =
"X-Schematic-Webhook-Signature"
TIMESTAMP_HEADER =
"X-Schematic-Webhook-Timestamp"

Class Method Summary collapse

Class Method Details

.compute_hex_signature(body, timestamp, secret) ⇒ Object



23
24
25
# File 'lib/schematic/webhook_verification.rb', line 23

def compute_hex_signature(body, timestamp, secret)
  compute_signature(body, timestamp, secret).unpack1("H*")
end

.compute_signature(body, timestamp, secret) ⇒ Object



17
18
19
20
21
# File 'lib/schematic/webhook_verification.rb', line 17

def compute_signature(body, timestamp, secret)
  body_str = body.is_a?(String) ? body : body.to_s
  message = "#{body_str}+#{timestamp}"
  OpenSSL::HMAC.digest("SHA256", secret, message)
end

.extract_body(request) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/schematic/webhook_verification.rb', line 66

def extract_body(request)
  if request.respond_to?(:raw_post)
    request.raw_post
  elsif request.respond_to?(:body)
    body = request.body
    if body.respond_to?(:read)
      content = body.read
      body.rewind if body.respond_to?(:rewind)
      content
    else
      body.to_s
    end
  else
    request.to_s
  end
end

.extract_header(request, header_name) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/schematic/webhook_verification.rb', line 53

def extract_header(request, header_name)
  if request.respond_to?(:get_header)
    # Rack-style request
    request.get_header("HTTP_#{header_name.upcase.tr("-", "_")}") ||
      request.get_header(header_name)
  elsif request.respond_to?(:[])
    # Hash-like headers
    request[header_name] ||
      request[header_name.downcase] ||
      request[header_name.upcase.tr("-", "_")]
  end
end

.secure_compare(expected, actual) ⇒ Object



47
48
49
50
51
# File 'lib/schematic/webhook_verification.rb', line 47

def secure_compare(expected, actual)
  return false unless expected.bytesize == actual.bytesize

  OpenSSL.fixed_length_secure_compare(expected, actual)
end

.verify_signature(payload, signature, timestamp, secret) ⇒ Object

rubocop:disable Naming/PredicateMethod



27
28
29
30
31
32
33
34
35
36
37
# File 'lib/schematic/webhook_verification.rb', line 27

def verify_signature(payload, signature, timestamp, secret) # rubocop:disable Naming/PredicateMethod
  raise MissingSignatureError, "missing webhook signature" if signature.nil? || signature.empty?
  raise MissingTimestampError, "missing webhook timestamp" if timestamp.nil? || timestamp.empty?

  expected_sig = compute_signature(payload, timestamp, secret)
  provided_sig = [signature].pack("H*")

  raise InvalidSignatureError, "invalid webhook signature" unless secure_compare(expected_sig, provided_sig)

  true
end

.verify_webhook_request(request, secret) ⇒ Object



39
40
41
42
43
44
45
# File 'lib/schematic/webhook_verification.rb', line 39

def verify_webhook_request(request, secret)
  signature = extract_header(request, SIGNATURE_HEADER)
  timestamp = extract_header(request, TIMESTAMP_HEADER)
  body = extract_body(request)

  verify_signature(body, signature, timestamp, secret)
end