UNPKG

@shopify/shopify-api

Version:

Shopify API Library for Node - accelerate development with support for authentication, graphql proxy, webhooks

130 lines (127 loc) 5.26 kB
import { logger } from '../logger/index.mjs'; import { validateHmacFromRequestFactory } from '../utils/hmac-validator.mjs'; import { HmacValidationType, ValidationErrorReason } from '../utils/types.mjs'; import { abstractConvertRequest } from '../../runtime/http/index.mjs'; import { WEBHOOK_HEADER_NAMES, WebhookType, WebhookValidationErrorReason } from './types.mjs'; import { topicForStorage } from './registry.mjs'; import { getHeader } from '../../runtime/http/headers.mjs'; function detectWebhookType(headers) { const eventsHmac = getHeader(headers, WEBHOOK_HEADER_NAMES[WebhookType.Events].hmac); if (eventsHmac) { return WebhookType.Events; } const webhooksHmac = getHeader(headers, WEBHOOK_HEADER_NAMES[WebhookType.Webhooks].hmac); if (webhooksHmac) { return WebhookType.Webhooks; } return WebhookType.Webhooks; } function validateFactory(config) { return async function validate({ rawBody, ...adapterArgs }) { const request = await abstractConvertRequest(adapterArgs); const webhookType = detectWebhookType(request.headers); const validHmacResult = await validateHmacFromRequestFactory(config)({ type: HmacValidationType.Webhook, rawBody, webhookType, ...adapterArgs, }); if (!validHmacResult.valid) { if (validHmacResult.reason === ValidationErrorReason.InvalidHmac) { const log = logger(config); await log.debug("Webhook HMAC validation failed. Please note that events manually triggered from a store's Notifications settings will fail this validation. To test this, please use the CLI or trigger the actual event in a development store."); } return validHmacResult; } return checkWebhookHeaders(request.headers, webhookType); }; } function getRequiredHeader(headers, headerName, missingHeaders) { const value = getHeader(headers, headerName); if (!value) { missingHeaders.push(headerName); } return value; } function checkWebhookHeaders(headers, webhookType) { if (webhookType === WebhookType.Webhooks) { return checkWebhooksHeaders(headers); } return checkEventsHeaders(headers); } function checkWebhooksHeaders(headers) { const headerNames = WEBHOOK_HEADER_NAMES[WebhookType.Webhooks]; const missingHeaders = []; const hmac = getRequiredHeader(headers, headerNames.hmac, missingHeaders); const topic = getRequiredHeader(headers, headerNames.topic, missingHeaders); const domain = getRequiredHeader(headers, headerNames.domain, missingHeaders); const apiVersion = getRequiredHeader(headers, headerNames.apiVersion, missingHeaders); const webhookId = getRequiredHeader(headers, headerNames.webhookId, missingHeaders); if (missingHeaders.length) { return { valid: false, reason: WebhookValidationErrorReason.MissingHeaders, missingHeaders, }; } const fields = { webhookType: WebhookType.Webhooks, hmac: hmac, topic: topicForStorage(topic), domain: domain, apiVersion: apiVersion, webhookId: webhookId, }; const subTopic = getHeader(headers, headerNames.subTopic); if (subTopic) fields.subTopic = subTopic; const name = getHeader(headers, headerNames.name); if (name) fields.name = name; const triggeredAt = getHeader(headers, headerNames.triggeredAt); if (triggeredAt) fields.triggeredAt = triggeredAt; const eventId = getHeader(headers, headerNames.eventId); if (eventId) fields.eventId = eventId; return { valid: true, ...fields }; } function checkEventsHeaders(headers) { const headerNames = WEBHOOK_HEADER_NAMES[WebhookType.Events]; const missingHeaders = []; const hmac = getRequiredHeader(headers, headerNames.hmac, missingHeaders); const topic = getRequiredHeader(headers, headerNames.topic, missingHeaders); const domain = getRequiredHeader(headers, headerNames.domain, missingHeaders); const apiVersion = getRequiredHeader(headers, headerNames.apiVersion, missingHeaders); const eventId = getRequiredHeader(headers, headerNames.eventId, missingHeaders); if (missingHeaders.length) { return { valid: false, reason: WebhookValidationErrorReason.MissingHeaders, missingHeaders, }; } const fields = { webhookType: WebhookType.Events, hmac: hmac, topic: topicForStorage(topic), domain: domain, apiVersion: apiVersion, eventId: eventId, }; const handle = getHeader(headers, headerNames.handle); if (handle) fields.handle = handle; const action = getHeader(headers, headerNames.action); if (action) fields.action = action; const resourceId = getHeader(headers, headerNames.resourceId); if (resourceId) fields.resourceId = resourceId; const triggeredAt = getHeader(headers, headerNames.triggeredAt); if (triggeredAt) fields.triggeredAt = triggeredAt; return { valid: true, ...fields }; } export { validateFactory }; //# sourceMappingURL=validate.mjs.map