Module: Whatsapp::WebhookSignature

Defined in:
lib/whatsapp/webhook_signature.rb

Overview

HMAC-SHA256 signature generation and verification for webhook payloads.

When Configuration#webhook_secret is set, the bridge signs all outgoing WebSocket events and the Rails middleware rejects payloads with missing or invalid signatures.

Examples:

Generating a signature

sig = Whatsapp::WebhookSignature.sign(payload, secret)

Verifying a signature

Whatsapp::WebhookSignature.verify!(payload, signature, secret)

Constant Summary collapse

HEADER =
"X-Whatsapp-Signature".freeze

Class Method Summary collapse

Class Method Details

.secure_compare(a, b) ⇒ Boolean

Constant-time string comparison to prevent timing attacks.

Parameters:

  • a (String)
  • b (String)

Returns:

  • (Boolean)


61
62
63
64
65
# File 'lib/whatsapp/webhook_signature.rb', line 61

def self.secure_compare(a, b)
  return false unless a.bytesize == b.bytesize

  OpenSSL.fixed_length_secure_compare(a, b)
end

.sign(payload, secret) ⇒ String

Sign a payload with HMAC-SHA256.

Parameters:

  • payload (String)

    raw request body

  • secret (String)

    shared secret

Returns:

  • (String)

    hex-encoded signature prefixed with "sha256="



24
25
26
27
# File 'lib/whatsapp/webhook_signature.rb', line 24

def self.sign(payload, secret)
  digest = OpenSSL::HMAC.hexdigest("SHA256", secret, payload)
  "sha256=#{digest}"
end

.valid?(payload, signature, secret) ⇒ Boolean

Verify a signature against a payload.

Parameters:

  • payload (String)

    raw request body

  • signature (String)

    signature to verify (e.g. "sha256=abc123...")

  • secret (String)

    shared secret

Returns:

  • (Boolean)


35
36
37
38
39
40
# File 'lib/whatsapp/webhook_signature.rb', line 35

def self.valid?(payload, signature, secret)
  return false if signature.nil? || signature.empty?

  expected = sign(payload, secret)
  secure_compare(expected, signature)
end

.verify!(payload, signature, secret) ⇒ true

Verify a signature, raising on failure.

Parameters:

  • payload (String)

    raw request body

  • signature (String)

    signature to verify

  • secret (String)

    shared secret

Returns:

  • (true)

Raises:



49
50
51
52
53
54
# File 'lib/whatsapp/webhook_signature.rb', line 49

def self.verify!(payload, signature, secret)
  raise WebhookSignatureError, "Missing webhook signature" if signature.nil? || signature.empty?
  raise WebhookSignatureError, "Invalid webhook signature" unless valid?(payload, signature, secret)

  true
end