n8n-nodes-base
Version:
Base nodes of n8n
65 lines • 2.72 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifySignature = verifySignature;
const crypto_1 = require("crypto");
/**
* Verifies the Zendesk webhook signature using HMAC-SHA256.
*
* Zendesk sends a signature in the `X-Zendesk-Webhook-Signature` header
* which is a base64-encoded HMAC-SHA256 digest of (timestamp + body).
*
* The timestamp is provided in the `X-Zendesk-Webhook-Signature-Timestamp` header.
*
* This function computes the expected signature using the stored webhook secret
* and compares it with the provided signature using a constant-time comparison.
*
* @returns true if signature is valid or no secret is configured, false otherwise
*/
function verifySignature() {
// Get the secret from workflow static data (set during webhook creation)
const webhookData = this.getWorkflowStaticData('node');
const webhookSecret = webhookData.webhookSecret;
// If no secret is configured, skip verification (backwards compatibility).
// This handles legacy webhooks created before signature verification was implemented.
// To enable verification, deactivate and reactivate the workflow.
if (!webhookSecret) {
return true;
}
const req = this.getRequestObject();
const headerData = this.getHeaderData();
// Get the signature and timestamp from Zendesk's headers
const signature = headerData['x-zendesk-webhook-signature'];
const timestamp = headerData['x-zendesk-webhook-signature-timestamp'];
if (!signature || !timestamp) {
return false;
}
try {
// Get the raw request body
if (!req.rawBody) {
return false;
}
// Compute: base64(HMACSHA256(TIMESTAMP + BODY))
const hmac = (0, crypto_1.createHmac)('sha256', webhookSecret);
let rawBodyString;
if (Buffer.isBuffer(req.rawBody)) {
rawBodyString = req.rawBody.toString('utf8');
}
else {
rawBodyString = typeof req.rawBody === 'string' ? req.rawBody : JSON.stringify(req.rawBody);
}
hmac.update(timestamp + rawBodyString);
const computedSignature = hmac.digest('base64');
// Constant-time comparison to prevent timing attacks
const computedBuffer = Buffer.from(computedSignature, 'utf8');
const providedBuffer = Buffer.from(signature, 'utf8');
// Buffers must be same length for timingSafeEqual
if (computedBuffer.length !== providedBuffer.length) {
return false;
}
return (0, crypto_1.timingSafeEqual)(computedBuffer, providedBuffer);
}
catch {
return false;
}
}
//# sourceMappingURL=ZendeskTriggerHelpers.js.map