JAVASCRIPT
Implement a Secure Webhook Listener in Node.js
Create a robust Express.js endpoint to receive and process real-time event notifications from various third-party services securely using webhook signatures.
// server.js
const express = require('express');
const crypto = require('crypto'); // Built-in Node.js module
const bodyParser = require('body-parser'); // npm install express body-parser
const app = express();
const PORT = process.env.PORT || 3002;
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || 'your_super_secret_key'; // Use a strong, unique secret
// IMPORTANT: Parse raw body for signature verification BEFORE JSON parsing
app.use(bodyParser.json({
verify: (req, res, buf) => {
req.rawBody = buf; // Store the raw body for signature verification
}
}));
app.post('/webhook/stripe', (req, res) => {
const signature = req.headers['stripe-signature']; // Or 'x-hub-signature', 'x-github-event', etc.
// Example: Stripe webhook verification (conceptually similar for others)
// You would typically use a library like 'stripe.webhooks.constructEvent'
// For custom webhooks, you might verify an HMAC signature
try {
// Basic example for HMAC-SHA256 signature verification
// This is highly dependent on the webhook provider's specific signature method
const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
hmac.update(req.rawBody); // Use the raw body, not the parsed JSON body
const expectedSignature = `t=${Math.floor(Date.now() / 1000)},v1=${hmac.digest('hex')}`; // Simplified, actual Stripe signature is more complex
// For simplicity, let's assume a direct match,
// but actual verification involves splitting the signature header,
// comparing timestamps to prevent replay attacks, and secure string comparison.
// if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) {
// throw new Error('Webhook signature verification failed.');
// }
// For this snippet, we'll just log if it exists and process,
// in a real app, robust signature verification is CRITICAL.
if (signature) {
console.log('Received webhook with signature. (Verification skipped for brevity)');
// In a real app: Verify signature here robustly.
} else {
console.warn('Received webhook without signature. Skipping verification.');
// In a real app, you might reject requests without signatures.
}
const event = req.body;
console.log('Received webhook event:', event.type);
// Process the webhook event based on its type
switch (event.type) {
case 'payment.succeeded':
console.log('Payment succeeded event:', event.data.object.id);
// Update database, send confirmation email, etc.
break;
case 'customer.created':
console.log('New customer created:', event.data.object.email);
break;
default:
console.log('Unhandled event type:', event.type);
}
res.status(200).send('Webhook received');
} catch (error) {
console.error('Webhook processing error:', error.message);
res.status(400).send('Webhook Error: ' + error.message);
}
});
app.listen(PORT, () => {
console.log(`Webhook listener running on port ${PORT}`);
});
How it works: This Node.js Express snippet sets up an endpoint (`/webhook/stripe`) to receive and process webhook events from a third-party API. Webhooks are a push-based mechanism, where the API notifies your server of events in real-time, eliminating the need for constant polling. The snippet includes essential security considerations like using `WEBHOOK_SECRET` for signature verification (though simplified for brevity), which ensures the request truly originated from the expected service and hasn't been tampered with. It also demonstrates how to parse and handle different event types.