UNPKG

@shopify/shopify-app-remix

Version:

Shopify Remix - to simplify the building of Shopify Apps with Remix

106 lines (93 loc) 2.97 kB
import {WebhookValidationErrorReason, WebhookType} from '@shopify/shopify-api'; import type {BasicParams} from '../../types'; import {adminClientFactory} from '../../clients'; import {handleClientErrorFactory} from '../admin/helpers'; import {ensureValidOfflineSession} from '../../helpers'; import type { AuthenticateWebhook, WebhookContext, WebhookContextWithoutSession, } from './types'; export function authenticateWebhookFactory<Topics extends string>( params: BasicParams, ): AuthenticateWebhook<Topics> { const {api, logger} = params; return async function authenticate( request: Request, ): Promise<WebhookContext<Topics>> { if (request.method !== 'POST') { logger.debug( 'Received a non-POST request for a webhook. Only POST requests are allowed.', {url: request.url, method: request.method}, ); throw new Response(undefined, { status: 405, statusText: 'Method not allowed', }); } const rawBody = await request.text(); const check = await api.webhooks.validate({ rawBody, rawRequest: request, }); if (!check.valid) { if (check.reason === WebhookValidationErrorReason.InvalidHmac) { logger.debug('Webhook HMAC validation failed', check); throw new Response(undefined, { status: 401, statusText: 'Unauthorized', }); } else { logger.debug('Webhook validation failed', check); throw new Response(undefined, {status: 400, statusText: 'Bad Request'}); } } const session = await ensureValidOfflineSession(params, check.domain); let webhookContext: WebhookContextWithoutSession<Topics>; if (check.webhookType === WebhookType.Webhooks) { webhookContext = { apiVersion: check.apiVersion, shop: check.domain, topic: check.topic as Topics, webhookId: check.webhookId, payload: JSON.parse(rawBody), subTopic: check.subTopic || undefined, session: undefined, admin: undefined, webhookType: check.webhookType, name: check.name, triggeredAt: check.triggeredAt, eventId: check.eventId, }; } else { webhookContext = { apiVersion: check.apiVersion, shop: check.domain, topic: check.topic as Topics, webhookId: check.webhookId, payload: JSON.parse(rawBody), session: undefined, admin: undefined, webhookType: check.webhookType, handle: check.handle, action: check.action, resourceId: check.resourceId, triggeredAt: check.triggeredAt, eventId: check.eventId, }; } if (!session) { return webhookContext; } const admin = adminClientFactory({ params, session, handleClientError: handleClientErrorFactory({request}), }); return { ...webhookContext, session, admin, }; }; }