Signature Verification
Verify the integrity and authenticity of incoming webhook deliveries.
Webhook Signature Verification
To protect your system against replay attacks and spoofing, Rehabify signs every webhook delivery using HMAC SHA-256. The resulting hex signature is sent in the X-Rehabify-Signature header.
Verification Algorithm
- Retrieve the raw HTTP request body string.
- Read the
X-Rehabify-Signatureheader value. - Compute the HMAC SHA-256 hash of the raw body using your organization's Webhook Secret (
whsec_...). - Compare your computed signature with the header signature using a timing-safe equality check.
Code Examples
Code
import crypto from 'crypto';
import type { NextRequest } from 'next/server';
export async function POST(req: NextRequest) {
const rawBody = await req.text();
const signature = req.headers.get('x-rehabify-signature');
const secret = process.env.REHABIFY_WEBHOOK_SECRET!;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('hex');
const isValid = crypto.timingSafeEqual(
Buffer.from(signature || '', 'hex'),
Buffer.from(expectedSignature, 'hex')
);
if (!isValid) {
return new Response('Invalid webhook signature', { status: 401 });
}
const event = JSON.parse(rawBody);
console.log('Verified Event Received:', event.event);
return new Response(JSON.stringify({ received: true }), { status: 200 });
}Code
import hmac
import hashlib
import os
from flask import Flask, request, jsonify
app = Flask(__name__)
WEBHOOK_SECRET = os.environ.get("REHABIFY_WEBHOOK_SECRET", "")
@app.route("/webhooks/rehabify", methods=["POST"])
def handle_webhook():
signature = request.headers.get("X-Rehabify-Signature", "")
raw_payload = request.get_data()
expected_signature = hmac.new(
WEBHOOK_SECRET.encode("utf-8"),
raw_payload,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected_signature):
return jsonify({"error": "Invalid signature"}), 401
data = request.json
print(f"Verified event: {data.get('event')}")
return jsonify({"received": True}), 200Code
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"io"
"net/http"
"os"
)
func webhookHandler(w http.ResponseWriter, r *http.Request) {
secret := []byte(os.Getenv("REHABIFY_WEBHOOK_SECRET"))
signature := r.Header.Get("X-Rehabify-Signature")
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Cannot read body", http.StatusBadRequest)
return
}
mac := hmac.New(sha256.New, secret)
mac.Write(body)
expectedSig := hex.EncodeToString(mac.Sum(nil))
if !hmac.Equal([]byte(signature), []byte(expectedSig)) {
http.Error(w, "Invalid signature", http.StatusUnauthorized)
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"received": true}`))
}