JAVASCRIPT

Create a Secure Webhook Endpoint with Signature Verification

Set up a Node.js Express webhook endpoint to securely receive real-time API updates, including signature verification for data integrity and authenticity.

const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');
const app = express();
const port = process.env.PORT || 3000;

// IMPORTANT: Use raw body parser for webhook signature verification
// This parses the body into a Buffer, required for HMAC hashing
app.use(bodyParser.json({
    verify: (req, res, buf) => {
        req.rawBody = buf; // Store the raw body for signature verification
    }
}));

const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || 'your_secret_key_here'; // Must match the secret configured in the API provider

// Function to verify the webhook signature
function verifyWebhookSignature(payload, signatureHeader, secret) {
    if (!signatureHeader) {
        return false; // No signature provided
    }

    // Example: GitHub style signature header 'sha256=<signature>'
    const parts = signatureHeader.split('=');
    if (parts.length !== 2 || parts[0] !== 'sha256') {
        console.error('Invalid signature header format:', signatureHeader);
        return false;
    }

    const algorithm = parts[0]; // e.g., sha256
    const providedSignature = parts[1];

    const hmac = crypto.createHmac(algorithm, secret);
    const digest = hmac.update(payload).digest('hex');

    // Compare the computed signature with the provided signature
    // Using crypto.timingSafeEqual to prevent timing attacks
    return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(providedSignature));
}

app.post('/webhook', (req, res) => {
    const signature = req.headers['x-hub-signature-256'] || req.headers['x-webhook-signature']; // Adjust header name as per API provider
    const rawPayload = req.rawBody; // The raw body buffer from bodyParser.json verify option

    if (!rawPayload) {
        console.error('Raw body not available for signature verification.');
        return res.status(400).send('Webhook error: Raw body missing.');
    }

    if (!verifyWebhookSignature(rawPayload, signature, WEBHOOK_SECRET)) {
        console.error('Webhook signature verification failed!');
        return res.status(401).send('Unauthorized: Invalid signature.');
    }

    console.log('Webhook signature verified successfully!');
    const eventData = req.body;
    console.log('Received webhook event:', eventData);

    // Process the eventData here
    // e.g., update database, trigger other services, etc.

    res.status(200).send('Webhook received and processed.');
});

app.listen(port, () => {
    console.log(`Webhook listener running on port ${port}`);
    console.log(`Access at http://localhost:${port}/webhook`);
});
How it works: This Node.js Express snippet sets up a robust webhook endpoint to receive real-time updates from an external API. Crucially, it includes signature verification using `crypto` to ensure that incoming requests are legitimate and haven't been tampered with. The `bodyParser.json` middleware is configured to store the raw request body, which is essential for calculating the HMAC signature. The `verifyWebhookSignature` function compares the generated signature with the one provided in the request headers (e.g., `X-Hub-Signature-256`), preventing unauthorized or malicious payloads from being processed.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs