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

Class Method Details

.blank?(v) ⇒ Boolean

Returns:

  • (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, "#{timestamp}.#{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?(timestamp) || blank?(signature)

  ts = begin
    Integer(timestamp.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