From 77cc676f4018b0759ef0fde3d9faa1b5d5262056 Mon Sep 17 00:00:00 2001 From: wirabasalamah Date: Sat, 23 May 2026 08:28:35 +0700 Subject: [PATCH] Handle Meta webhook signature on default endpoint in dev --- backend/src/webhooks/webhooks.service.ts | 15 ++++++++++++--- backend/src/webhooks/webhooks.utils.ts | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/backend/src/webhooks/webhooks.service.ts b/backend/src/webhooks/webhooks.service.ts index 3d5208c..69db412 100644 --- a/backend/src/webhooks/webhooks.service.ts +++ b/backend/src/webhooks/webhooks.service.ts @@ -235,14 +235,22 @@ export class WebhooksService { const normalizedProvider = provider.toLowerCase(); const metaSignature = this.readHeader(headers['x-hub-signature-256']); const genericSecret = this.readHeader(headers['x-webhook-secret']); + const isMetaSignatureFlow = normalizedProvider === 'meta' || normalizedProvider === 'default'; + const hasMetaSignature = !!metaSignature; - if (normalizedProvider === 'meta' && config.appSecret) { + if (isMetaSignatureFlow && config.appSecret && hasMetaSignature) { if (!rawBody || !metaSignature) { throw new UnauthorizedException('Missing meta webhook signature'); } verifyMetaSignature(rawBody, metaSignature, config.appSecret); - return { verified: true, reason: 'meta-signature' }; + return { + verified: true, + reason: + normalizedProvider === 'meta' + ? 'meta-signature' + : 'meta-signature-on-default-endpoint', + }; } if (genericSecret) { @@ -253,7 +261,7 @@ export class WebhooksService { return { verified: true, reason: 'shared-secret' }; } - if (config.allowUnsigned) { + if (config.allowUnsigned || !config.isProduction) { return { verified: false, reason: 'unsigned-development-request' }; } @@ -281,6 +289,7 @@ export class WebhooksService { ? storedJson.appSecret : env.metaWebhookAppSecret, allowUnsigned: env.webhookAllowUnsigned, + isProduction: env.isProduction, subscriptions: Array.isArray(storedJson.subscriptions) && storedJson.subscriptions.length > 0 ? storedJson.subscriptions.filter((item): item is string => typeof item === 'string') diff --git a/backend/src/webhooks/webhooks.utils.ts b/backend/src/webhooks/webhooks.utils.ts index cd8f17d..671e2a2 100644 --- a/backend/src/webhooks/webhooks.utils.ts +++ b/backend/src/webhooks/webhooks.utils.ts @@ -203,7 +203,7 @@ export function normalizeWebhookPayload(provider: string, payload: unknown) { const normalizedProvider = provider.toLowerCase(); if ( - normalizedProvider === 'meta' && + (normalizedProvider === 'meta' || normalizedProvider === 'default') && readString(payloadRecord.object) === 'whatsapp_business_account' ) { const metaEvents = buildMetaEvents(payloadRecord, normalizedProvider);