timestamp = request.headers.fetch('X-Signature-Timestamp')
signature = request.headers.fetch('X-Signature')
payload = request.raw_post
data = "#{timestamp}.#{payload}"
expected = OpenSSL::HMAC.hexdigest('SHA256', ENV.fetch('WEBHOOK_SECRET'), data)
event_id = request.headers.fetch('X-Event-Id')
timestamp = request.headers.fetch('X-Signature-Timestamp').to_i
raise ActionController::BadRequest, 'stale request' if Time.now.to_i - timestamp > 300
raise ActionController::BadRequest, 'replay detected' if WebhookEvent.exists?(external_id: event_id)
import crypto from 'crypto';
interface VerifyOptions {
rawBody: Buffer;
signatureHeader: string | undefined;
secret: string;
require 'sinatra/base'
require 'json'
require_relative 'signature_verifier'
class WebhookApp < Sinatra::Base
configure do
const crypto = require('crypto');
function computeSignature(secret, timestamp, payload) {
return crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${payload}`, 'utf8')
package session
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
import hashlib
import hmac
import time
class SignatureError(Exception):
class User < ApplicationRecord
has_secure_password
RESET_TOKEN_TTL = 30.minutes
def self.reset_verifier
class WebhookSignature
class VerificationError < StandardError; end
TOLERANCE = 300 # seconds
def initialize(payload:, header:, secrets:)
package webhook
import (
"encoding/json"
"io"
"net/http"
import hmac
import hashlib
import time
from fastapi import Request, HTTPException, status