Module: Cloudflare::Email::Verification
- Defined in:
- lib/cloudflare/email/verification.rb
Overview
HMAC verification for inbound webhook signatures from the bundled Cloudflare Email Worker. Pure-Ruby and Rails-free so it can be unit-tested in isolation.
Worker signs: HMAC-SHA256(secret, “timestamp.raw_body”) Worker sends:
X-CF-Email-Timestamp: <unix seconds>
X-CF-Email-Signature: <hex digest>
Constant Summary collapse
- DEFAULT_WINDOW =
seconds
5 * 60
Class Method Summary collapse
- .blank?(v) ⇒ Boolean
- .sign(secret:, body:, timestamp:) ⇒ Object
-
.verify(secret:, body:, timestamp:, signature:, window: DEFAULT_WINDOW, now: Time.now.to_i) ⇒ Object
Returns :ok, :bad_signature, or :stale.
Class Method Details
.blank?(v) ⇒ Boolean
39 40 41 |
# File 'lib/cloudflare/email/verification.rb', line 39 def self.blank?(v) v.nil? || v.to_s.empty? end |
.sign(secret:, body:, timestamp:) ⇒ Object
35 36 37 |
# File 'lib/cloudflare/email/verification.rb', line 35 def self.sign(secret:, body:, timestamp:) Signing.hmac_hex(secret, "#{}.#{body}") end |
.verify(secret:, body:, timestamp:, signature:, window: DEFAULT_WINDOW, now: Time.now.to_i) ⇒ Object
Returns :ok, :bad_signature, or :stale. Returns :bad_signature for any malformed input.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/cloudflare/email/verification.rb', line 18 def self.verify(secret:, body:, timestamp:, signature:, window: DEFAULT_WINDOW, now: Time.now.to_i) return :bad_signature if blank?(secret) || blank?(body) || blank?() || blank?(signature) ts = begin Integer(.to_s, 10) rescue ArgumentError, TypeError return :bad_signature end return :stale if (now - ts).abs > window expected = Signing.hmac_hex(secret, "#{ts}.#{body}") return :bad_signature unless Signing.secure_compare(expected, signature.to_s) :ok end |