@shopify/shopify-app-remix
Version:
Shopify Remix - to simplify the building of Shopify Apps with Remix
106 lines (93 loc) • 2.97 kB
text/typescript
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,
};
};
}