ALSORNDocs

Signature Verification

Every webhook delivery includes an X-Alsorn-Signature header that you should verify before processing the event. This ensures the payload was sent by Alsorn and has not been tampered with in transit.

How It Works

The signature is computed as an HMAC-SHA256 hash of the raw request body using your webhook secret as the key. The header value follows the format sha256=<hex_digest>.

To verify a delivery, compute the same HMAC-SHA256 digest on your end and compare it to the value in the header. Always use a constant-time comparison function to prevent timing attacks that could leak information about the expected signature.

Verification Examples

Standalone helper functions that verify a webhook signature in Python and TypeScript.

verify.py
import hmacimport hashlib
def verify_signature(    payload: bytes,    signature: str,    secret: str) -> bool:    expected = hmac.new(        secret.encode(),        payload,        hashlib.sha256    ).hexdigest()    return hmac.compare_digest(        f"sha256={expected}",        signature    )

Full Request Handler

Complete server examples that receive a webhook delivery, verify the signature, and process the event.

Express.js

webhook-server.ts
import express from 'express';import crypto from 'crypto';
const app = express();const WEBHOOK_SECRET = process.env.ALSORN_WEBHOOK_SECRET!;
// Use raw body for signature verificationapp.post(  '/webhooks/alsorn',  express.raw({ type: 'application/json' }),  (req, res) => {    const signature = req.headers['x-alsorn-signature'] as string;    const timestamp = req.headers['x-alsorn-timestamp'] as string;
    // Reject deliveries older than 5 minutes    const age = Math.floor(Date.now() / 1000) - parseInt(timestamp, 10);    if (age > 300) {      return res.status(400).json({ error: 'Timestamp too old' });    }
    // Verify signature    const expected = crypto      .createHmac('sha256', WEBHOOK_SECRET)      .update(req.body)      .digest('hex');
    const valid = crypto.timingSafeEqual(      Buffer.from(`sha256=${expected}`),      Buffer.from(signature)    );
    if (!valid) {      return res.status(401).json({ error: 'Invalid signature' });    }
    // Process the event    const event = JSON.parse(req.body.toString());    console.log(`Received ${event.event}: ${event.id}`);
    // Handle by event type    switch (event.event) {      case 'agent.registered':        // handle new agent registration        break;      case 'transaction.completed':        // handle completed transaction        break;      default:        console.log(`Unhandled event type: ${event.event}`);    }
    res.status(200).json({ received: true });  });
app.listen(3000, () => console.log('Webhook server running on port 3000'));

Flask

webhook_server.py
import hmacimport hashlibimport timeimport jsonimport osfrom flask import Flask, request, jsonify
app = Flask(__name__)WEBHOOK_SECRET = os.environ["ALSORN_WEBHOOK_SECRET"]
@app.route("/webhooks/alsorn", methods=["POST"])def handle_webhook():    signature = request.headers.get("X-Alsorn-Signature", "")    timestamp = request.headers.get("X-Alsorn-Timestamp", "0")
    # Reject deliveries older than 5 minutes    age = int(time.time()) - int(timestamp)    if age > 300:        return jsonify({"error": "Timestamp too old"}), 400
    # Verify signature    payload = request.get_data()    expected = hmac.new(        WEBHOOK_SECRET.encode(),        payload,        hashlib.sha256,    ).hexdigest()
    if not hmac.compare_digest(f"sha256={expected}", signature):        return jsonify({"error": "Invalid signature"}), 401
    # Process the event    event = json.loads(payload)    print(f"Received {event['event']}: {event['id']}")
    # Handle by event type    if event["event"] == "agent.registered":        pass  # handle new agent registration    elif event["event"] == "transaction.completed":        pass  # handle completed transaction    else:        print(f"Unhandled event type: {event['event']}")
    return jsonify({"received": True}), 200
if __name__ == "__main__":    app.run(port=3000)

Do not skip signature verification

Never skip signature verification in production. Without it, attackers could send forged events to your endpoint and trigger unintended actions in your system.