Webhooks
Signature Validation

Signature Validation

When you create a webhook in the bitvora console, you will be given a secret. This secret is used to sign the webhook payload. You can use this secret to validate the webhook signature.

Example Signature Validation

Node.js

validateWebhookSignature(
      payload: string,
      signature: string,
      secret: string
    ): boolean {
      const hmac = createHmac("sha256", secret);
      hmac.update(payload);
      const hash = hmac.digest("hex");
      return hash === signature;
    }
 
    // inside a controller
    handleBitvoraWebhook(@Body() body: any, @Req() req: any): string {
    let signature = req.headers['bitvora-signature'];
    let bitvora = new BitvoraClient('api_key', 'signet');
    let stringBody = JSON.stringify(body);
    console.log(stringBody);
    if (
      validateWebhookSignature(
        stringBody,
        signature,
        '8a83fed4f7c7fed7e57f7c2fe6fa5e9dfd38dfab56b3498dc76e8d43241daa5c', // example secret
      )
    ) {
      console.log('Invalid signature');
    } else {
      console.log('Valid signature');
    }
    console.log('Received webhook', body);
    console.log('Signature', signature);
    return this.appService.handleBitvoraWebhook(body);
  }

Go

package main
 
import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"net/http"
	"io/ioutil"
)
 
func validateWebhookSignature(payload, signature, secret string) bool {
	h := hmac.New(sha256.New, []byte(secret))
	h.Write([]byte(payload))
	expectedSignature := hex.EncodeToString(h.Sum(nil))
	return expectedSignature == signature
}
 
func handleBitvoraWebhook(w http.ResponseWriter, r *http.Request) {
	secret := "8a83fed4f7c7fed7e57f7c2fe6fa5e9dfd38dfab56b3498dc76e8d43241daa5c" // example secret
	signature := r.Header.Get("bitvora-signature")
 
	bodyBytes, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Error reading request body", http.StatusBadRequest)
		return
	}
	payload := string(bodyBytes)
 
	if validateWebhookSignature(payload, signature, secret) {
		fmt.Println("Valid signature")
		// Process webhook
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("Webhook received"))
	} else {
		fmt.Println("Invalid signature")
		http.Error(w, "Invalid signature", http.StatusUnauthorized)
	}
}
 
func main() {
	http.HandleFunc("/webhook", handleBitvoraWebhook)
	http.ListenAndServe(":8080", nil)
}

PHP

<?php
 
function validateWebhookSignature($payload, $signature, $secret) {
    $hash = hash_hmac('sha256', $payload, $secret);
    return hash_equals($hash, $signature);
}
 
function handleBitvoraWebhook() {
    $secret = "8a83fed4f7c7fed7e57f7c2fe6fa5e9dfd38dfab56b3498dc76e8d43241daa5c"; // example secret
    $signature = $_SERVER['HTTP_BITVORA_SIGNATURE'];
    $payload = file_get_contents('php://input');
 
    if (validateWebhookSignature($payload, $signature, $secret)) {
        error_log("Valid signature");
        // Process webhook
        http_response_code(200);
        echo "Webhook received";
    } else {
        error_log("Invalid signature");
        http_response_code(401);
        echo "Invalid signature";
    }
}
 
handleBitvoraWebhook();
?>

Python

import hmac
import hashlib
from flask import Flask, request, jsonify
 
app = Flask(__name__)
 
def validate_webhook_signature(payload, signature, secret):
    hash = hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(hash, signature)
 
@app.route('/webhook', methods=['POST'])
def handle_bitvora_webhook():
    secret = "8a83fed4f7c7fed7e57f7c2fe6fa5e9dfd38dfab56b3498dc76e8d43241daa5c"  # example secret
    signature = request.headers.get('bitvora-signature')
    payload = request.get_data(as_text=True)
 
    if validate_webhook_signature(payload, signature, secret):
        print("Valid signature")
        # Process webhook
        return jsonify({"message": "Webhook received"}), 200
    else:
        print("Invalid signature")
        return jsonify({"message": "Invalid signature"}), 401
 
if __name__ == '__main__':
    app.run(port=8080)