Module: Blockchain0x::Webhooks
- Defined in:
- lib/blockchain0x/webhooks.rb
Defined Under Namespace
Classes: Result
Constant Summary collapse
- SIG_HEADER =
'X-Blockchain0x-Signature'- TS_HEADER =
'X-Blockchain0x-Timestamp'- TYPE_HEADER =
'X-Blockchain0x-Event-Type'- EVENT_ID_HEADER =
'X-Blockchain0x-Event-Id'- DELIVERY_ID_HEADER =
'X-Blockchain0x-Delivery-Id'- DEFAULT_TOLERANCE_SECONDS =
300- SIGNATURE_MISSING =
Failure-code constants mirror @blockchain0x/node + Python + Go.
'webhook.signature_missing'- SIGNATURE_MALFORMED =
'webhook.signature_malformed'- TIMESTAMP_OUTSIDE_WINDOW =
'webhook.timestamp_outside_window'- SIGNATURE_MISMATCH =
'webhook.signature_mismatch'- SECRET_MISSING =
'webhook.secret_missing'- TIMESTAMP_MISSING =
'webhook.timestamp_missing'- TIMESTAMP_INVALID =
'webhook.timestamp_invalid'
Class Method Summary collapse
-
.verify(headers:, raw_body:, secret:, tolerance_seconds: DEFAULT_TOLERANCE_SECONDS, now: nil, raise_on_fail: false) ⇒ Result
Discriminated-union result (default path).
Class Method Details
.verify(headers:, raw_body:, secret:, tolerance_seconds: DEFAULT_TOLERANCE_SECONDS, now: nil, raise_on_fail: false) ⇒ Result
Returns discriminated-union result (default path).
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/blockchain0x/webhooks.rb', line 81 def verify(headers:, raw_body:, secret:, tolerance_seconds: DEFAULT_TOLERANCE_SECONDS, now: nil, raise_on_fail: false) if secret.nil? || secret.empty? return fail_result(SECRET_MISSING, 'missing webhook secret', raise_on_fail) end raw_sig = pick_header(headers, SIG_HEADER) if raw_sig.nil? return fail_result(SIGNATURE_MISSING, 'missing X-Blockchain0x-Signature header', raise_on_fail) end parsed = parse_signature(raw_sig) return fail_result(SIGNATURE_MALFORMED, 'malformed signature header', raise_on_fail) if parsed.nil? ts_from_header, sig_hex = parsed ts = ts_from_header if ts.nil? raw_ts = pick_header(headers, TS_HEADER) return fail_result(TIMESTAMP_MISSING, 'missing X-Blockchain0x-Timestamp header', raise_on_fail) if raw_ts.nil? begin ts = Integer(raw_ts) rescue ArgumentError, TypeError return fail_result(TIMESTAMP_INVALID, 'invalid X-Blockchain0x-Timestamp value', raise_on_fail) end end current = now || Time.now.to_i if (current - ts).abs > tolerance_seconds return fail_result(TIMESTAMP_OUTSIDE_WINDOW, 'timestamp outside tolerance window', raise_on_fail) end want = OpenSSL::HMAC.hexdigest('SHA256', secret, "#{ts}.#{raw_body}") unless secure_compare(want, sig_hex.downcase) return fail_result(SIGNATURE_MISMATCH, 'signature mismatch', raise_on_fail) end Result.new( ok: true, code: nil, event_type: pick_header(headers, TYPE_HEADER), event_id: pick_header(headers, EVENT_ID_HEADER), delivery_id: pick_header(headers, DELIVERY_ID_HEADER), ) end |