philiprehberger-webhook_signature

Tests Gem Version Last updated

HMAC-SHA256 webhook signing and verification with replay prevention

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-webhook_signature"

Or install directly:

gem install philiprehberger-webhook_signature

Usage

Signing (sender side)

require "philiprehberger/webhook_signature"

secret = "whsec_your_secret_key"
payload = '{"event":"order.created","data":{"id":123}}'

# Quick sign
result = Philiprehberger::WebhookSignature.sign(payload, secret: secret)
# => { timestamp: 1710000000, signature: "a1b2c3..." }

# Or use a Signer instance
signer = Philiprehberger::WebhookSignature::Signer.new(secret)
header = signer.sign_header(payload)
# => "t=1710000000,v1=a1b2c3..."

Verification (receiver side)

secret = "whsec_your_secret_key"

# Quick verify
valid = Philiprehberger::WebhookSignature.verify(
  payload,
  secret: secret,
  timestamp: params[:timestamp],
  signature: params[:signature]
)

# Or use a Verifier instance
verifier = Philiprehberger::WebhookSignature::Verifier.new(secret)

# Verify from components
verifier.verify(payload, timestamp: ts, signature: sig)

# Verify from header string
verifier.verify_header(payload, header: "t=1710000000,v1=a1b2c3...")

# Verify and raise on failure
verifier.verify!(payload, timestamp: ts, signature: sig)
# raises VerificationError on failure

# Verify header and raise on failure
verifier.verify_header!(payload, header: "t=1710000000,v1=a1b2c3...")
# raises VerificationError on failure

# Boolean helpers (return true/false, never raise)
verifier.valid?(payload, timestamp: ts, signature: sig)
verifier.valid_header?(payload, header: "t=1710000000,v1=a1b2c3...")

Choosing an HMAC Algorithm

# Default is :sha256 — existing code is unaffected
signer = Philiprehberger::WebhookSignature::Signer.new(secret)

# Opt in to SHA-512 for a longer digest
signer = Philiprehberger::WebhookSignature::Signer.new(secret, algorithm: :sha512)
verifier = Philiprehberger::WebhookSignature::Verifier.new(secret, algorithm: :sha512)

# Or via the module-level helpers
Philiprehberger::WebhookSignature.sign(payload, secret: secret, algorithm: :sha512)
Philiprehberger::WebhookSignature.verify(
  payload, secret: secret, timestamp: ts, signature: sig, algorithm: :sha512
)

# Unsupported values raise ArgumentError listing the supported symbols
Philiprehberger::WebhookSignature::Signer.new(secret, algorithm: :md5)
# => ArgumentError: Unsupported algorithm: :md5. Supported algorithms: :sha256, :sha512

Replay Prevention

verifier = Philiprehberger::WebhookSignature::Verifier.new(secret)

# Default: 300 seconds (5 minutes) tolerance
verifier.verify(payload, timestamp: old_ts, signature: sig)
# => false (if timestamp is too old)

# Custom tolerance
verifier.verify(payload, timestamp: ts, signature: sig, tolerance: 600)

# Disable replay check
verifier.verify(payload, timestamp: ts, signature: sig, tolerance: nil)

API

Method / Class Description
WebhookSignature.sign(payload, secret:, timestamp:, algorithm:) Sign a payload
WebhookSignature.verify(payload, secret:, timestamp:, signature:, tolerance:, algorithm:) Verify a payload
Signer.new(secret, algorithm:) Create a signer (algorithm defaults to :sha256; :sha512 also supported)
Signer#sign(payload, timestamp:) Sign, returns hash
Signer#sign_header(payload, timestamp:) Sign, returns header string
Verifier.new(secret, algorithm:) Create a verifier (algorithm defaults to :sha256; :sha512 also supported)
Verifier#verify(payload, timestamp:, signature:, tolerance:) Verify, returns boolean
Verifier#verify_header(payload, header:, tolerance:) Verify a header string
Verifier#verify!(payload, timestamp:, signature:, tolerance:) Verify or raise
Verifier#verify_header!(payload, header:, tolerance:) Verify a header string or raise
Verifier#valid?(payload, timestamp:, signature:, tolerance:) Boolean verify (never raises)
Verifier#valid_header?(payload, header:, tolerance:) Boolean header verify (never raises)

Development

bundle install
bundle exec rspec
bundle exec rubocop

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT