Class: Nombaone::Webhooks
- Inherits:
-
Object
- Object
- Nombaone::Webhooks
- Defined in:
- lib/nombaone/webhooks.rb,
sig/nombaone/webhooks.rbs
Overview
Verify and parse incoming NombaOne webhook deliveries.
Available as nombaone.webhooks on a client, or standalone via
webhooks — verification needs only the endpoint's signing secret,
never an API key, so it works in a receiver that never builds a client.
Feed it the raw request body. Parsing and re-serializing JSON can
reorder keys and change bytes, which breaks the signature. Capture the body
before any middleware parses it (request.raw_post in Rack/Rails).
Constant Summary collapse
- DEFAULT_TOLERANCE_SECONDS =
Maximum allowed age (seconds) between the delivery's
ttimestamp and now. 300
Instance Method Summary collapse
-
#construct_event(payload, signature_header, secret, tolerance: DEFAULT_TOLERANCE_SECONDS) ⇒ WebhookEvent
Verify a delivery's signature and timestamp, then parse and return the typed event.
-
#generate_test_header(payload:, secret:, timestamp: nil) ⇒ String
Build a valid
X-Nombaone-Signatureheader for a payload — for testing your own handler without waiting on a real delivery. -
#verify_signature(payload, signature_header, secret, tolerance: DEFAULT_TOLERANCE_SECONDS) ⇒ true
Verify a delivery's signature and timestamp only (no parse).
Instance Method Details
#construct_event(payload, signature_header, secret, tolerance: DEFAULT_TOLERANCE_SECONDS) ⇒ WebhookEvent
Verify a delivery's signature and timestamp, then parse and return the typed event. This is the one call your handler needs.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/nombaone/webhooks.rb', line 45 def construct_event(payload, signature_header, secret, tolerance: DEFAULT_TOLERANCE_SECONDS) verify_signature(payload, signature_header, secret, tolerance: tolerance) begin parsed = JSON.parse(payload.to_s) rescue JSON::ParserError raise WebhookVerificationError, "Webhook payload was not valid JSON." end unless parsed.is_a?(Hash) raise WebhookVerificationError, "Webhook payload was not a JSON object." end WebhookEvent.new(ensure_event_block(parsed)) end |
#generate_test_header(payload:, secret:, timestamp: nil) ⇒ String
Build a valid X-Nombaone-Signature header for a payload — for testing
your own handler without waiting on a real delivery.
101 102 103 104 |
# File 'lib/nombaone/webhooks.rb', line 101 def generate_test_header(payload:, secret:, timestamp: nil) = ( || Time.now.to_i).to_s "t=#{},v1=#{compute_signature(secret, , payload.to_s)}" end |
#verify_signature(payload, signature_header, secret, tolerance: DEFAULT_TOLERANCE_SECONDS) ⇒ true
Verify a delivery's signature and timestamp only (no parse). Returns true on success; raises with a distinct message per failure mode.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/nombaone/webhooks.rb', line 69 def verify_signature(payload, signature_header, secret, tolerance: DEFAULT_TOLERANCE_SECONDS) if signature_header.nil? || signature_header.empty? raise WebhookVerificationError, "Missing X-Nombaone-Signature header — is this request really from NombaOne?" end if secret.nil? || secret.empty? raise WebhookVerificationError, "Missing signing secret — pass the secret shown when the endpoint was created." end , signatures = parse_signature_header(signature_header) assert_within_tolerance(, tolerance) expected = compute_signature(secret, , payload.to_s) return true if signatures.any? { |candidate| secure_compare(candidate, expected) } raise WebhookVerificationError, "Webhook signature verification failed — check you are using this endpoint's " \ "current signing secret and the exact raw request body (no re-serialization)." end |