Handle Meta webhook signature on default endpoint in dev

This commit is contained in:
2026-05-23 08:28:35 +07:00
parent 8a7d0103ee
commit 77cc676f40
2 changed files with 13 additions and 4 deletions

View File

@ -235,14 +235,22 @@ export class WebhooksService {
const normalizedProvider = provider.toLowerCase(); const normalizedProvider = provider.toLowerCase();
const metaSignature = this.readHeader(headers['x-hub-signature-256']); const metaSignature = this.readHeader(headers['x-hub-signature-256']);
const genericSecret = this.readHeader(headers['x-webhook-secret']); 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) { if (!rawBody || !metaSignature) {
throw new UnauthorizedException('Missing meta webhook signature'); throw new UnauthorizedException('Missing meta webhook signature');
} }
verifyMetaSignature(rawBody, metaSignature, config.appSecret); 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) { if (genericSecret) {
@ -253,7 +261,7 @@ export class WebhooksService {
return { verified: true, reason: 'shared-secret' }; return { verified: true, reason: 'shared-secret' };
} }
if (config.allowUnsigned) { if (config.allowUnsigned || !config.isProduction) {
return { verified: false, reason: 'unsigned-development-request' }; return { verified: false, reason: 'unsigned-development-request' };
} }
@ -281,6 +289,7 @@ export class WebhooksService {
? storedJson.appSecret ? storedJson.appSecret
: env.metaWebhookAppSecret, : env.metaWebhookAppSecret,
allowUnsigned: env.webhookAllowUnsigned, allowUnsigned: env.webhookAllowUnsigned,
isProduction: env.isProduction,
subscriptions: subscriptions:
Array.isArray(storedJson.subscriptions) && storedJson.subscriptions.length > 0 Array.isArray(storedJson.subscriptions) && storedJson.subscriptions.length > 0
? storedJson.subscriptions.filter((item): item is string => typeof item === 'string') ? storedJson.subscriptions.filter((item): item is string => typeof item === 'string')

View File

@ -203,7 +203,7 @@ export function normalizeWebhookPayload(provider: string, payload: unknown) {
const normalizedProvider = provider.toLowerCase(); const normalizedProvider = provider.toLowerCase();
if ( if (
normalizedProvider === 'meta' && (normalizedProvider === 'meta' || normalizedProvider === 'default') &&
readString(payloadRecord.object) === 'whatsapp_business_account' readString(payloadRecord.object) === 'whatsapp_business_account'
) { ) {
const metaEvents = buildMetaEvents(payloadRecord, normalizedProvider); const metaEvents = buildMetaEvents(payloadRecord, normalizedProvider);