next-sanity
Version:
Sanity.io toolkit for Next.js
1 lines • 9.18 kB
Source Map (JSON)
{"version":3,"file":"webhook.cjs","sources":["../../../node_modules/.pnpm/@sanity+webhook@4.0.4/node_modules/@sanity/webhook/dist/index.mjs","../src/webhook/index.ts"],"sourcesContent":["class WebhookSignatureValueError extends Error {\n type = \"WebhookSignatureValueError\";\n statusCode = 401;\n}\nclass WebhookSignatureFormatError extends Error {\n type = \"WebhookSignatureFormatError\";\n statusCode = 400;\n}\nfunction isSignatureError(error) {\n return typeof error == \"object\" && error !== null && \"type\" in error && [\"WebhookSignatureValueError\", \"WebhookSignatureFormatError\"].includes(\n error.type\n );\n}\nconst MINIMUM_TIMESTAMP = 16094592e5, SIGNATURE_HEADER_REGEX = /^t=(\\d+)[, ]+v1=([^, ]+)$/, SIGNATURE_HEADER_NAME = \"sanity-webhook-signature\";\nasync function assertValidSignature(stringifiedPayload, signature, secret) {\n const { timestamp } = decodeSignatureHeader(signature), encoded = await encodeSignatureHeader(stringifiedPayload, timestamp, secret);\n if (signature !== encoded)\n throw new WebhookSignatureValueError(\"Signature is invalid\");\n}\nasync function isValidSignature(stringifiedPayload, signature, secret) {\n try {\n return await assertValidSignature(stringifiedPayload, signature, secret), !0;\n } catch (err) {\n if (isSignatureError(err))\n return !1;\n throw err;\n }\n}\nasync function assertValidRequest(request, secret) {\n const signature = request.headers[SIGNATURE_HEADER_NAME];\n if (Array.isArray(signature))\n throw new WebhookSignatureFormatError(\"Multiple signature headers received\");\n if (typeof signature != \"string\")\n throw new WebhookSignatureValueError(\"Request contained no signature header\");\n if (typeof request.body > \"u\")\n throw new WebhookSignatureFormatError(\"Request contained no parsed request body\");\n if (typeof request.body == \"string\" || Buffer.isBuffer(request.body))\n await assertValidSignature(request.body.toString(\"utf8\"), signature, secret);\n else\n throw new Error(\n \"[@sanity/webhook] `request.body` was not a string/buffer - this can lead to invalid signatures. See the [migration docs](https://github.com/sanity-io/webhook-toolkit#from-parsed-to-unparsed-body) for details on how to fix this.\"\n );\n}\nasync function isValidRequest(request, secret) {\n try {\n return await assertValidRequest(request, secret), !0;\n } catch (err) {\n if (isSignatureError(err))\n return !1;\n throw err;\n }\n}\nasync function encodeSignatureHeader(stringifiedPayload, timestamp, secret) {\n const signature = await createHS256Signature(stringifiedPayload, timestamp, secret);\n return `t=${timestamp},v1=${signature}`;\n}\nfunction decodeSignatureHeader(signaturePayload) {\n if (!signaturePayload)\n throw new WebhookSignatureFormatError(\"Missing or empty signature header\");\n const [, timestamp, hashedPayload] = signaturePayload.trim().match(SIGNATURE_HEADER_REGEX) || [];\n if (!timestamp || !hashedPayload)\n throw new WebhookSignatureFormatError(\"Invalid signature payload format\");\n return {\n timestamp: parseInt(timestamp, 10),\n hashedPayload\n };\n}\nasync function createHS256Signature(stringifiedPayload, timestamp, secret) {\n if (typeof crypto > \"u\")\n throw new TypeError(\n \"The Web Crypto API is not available in this environment, either polyfill `globalThis.crypto` or downgrade to `@sanity/webhook@3` which uses the Node.js `crypto` module.\"\n );\n if (!secret || typeof secret != \"string\")\n throw new WebhookSignatureFormatError(\"Invalid secret provided\");\n if (!stringifiedPayload)\n throw new WebhookSignatureFormatError(\"Can not create signature for empty payload\");\n if (typeof stringifiedPayload != \"string\")\n throw new WebhookSignatureFormatError(\"Payload must be a JSON-encoded string\");\n if (typeof timestamp != \"number\" || isNaN(timestamp) || timestamp < MINIMUM_TIMESTAMP)\n throw new WebhookSignatureFormatError(\n \"Invalid signature timestamp, must be a unix timestamp with millisecond precision\"\n );\n const enc = new TextEncoder(), key = await crypto.subtle.importKey(\n \"raw\",\n enc.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n !1,\n [\"sign\"]\n ), signaturePayload = `${timestamp}.${stringifiedPayload}`, signature = await crypto.subtle.sign(\"HMAC\", key, enc.encode(signaturePayload)), signatureArray = Array.from(new Uint8Array(signature));\n return btoa(String.fromCharCode.apply(null, signatureArray)).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\nfunction requireSignedRequest(options) {\n const parseBody = typeof options.parseBody > \"u\" ? !0 : options.parseBody, respondOnError = typeof options.respondOnError > \"u\" ? !0 : options.respondOnError;\n return async function(request, response, next) {\n try {\n await assertValidRequest(request, options.secret), parseBody && typeof request.body == \"string\" && (request.body = JSON.parse(request.body)), next();\n } catch (err) {\n if (!respondOnError || !isSignatureError(err)) {\n next(err);\n return;\n }\n response.status(err.statusCode).json({ message: err.message });\n }\n };\n}\nexport {\n SIGNATURE_HEADER_NAME,\n WebhookSignatureFormatError,\n WebhookSignatureValueError,\n assertValidRequest,\n assertValidSignature,\n decodeSignatureHeader,\n encodeSignatureHeader,\n isSignatureError,\n isValidRequest,\n isValidSignature,\n requireSignedRequest\n};\n//# sourceMappingURL=index.mjs.map\n","import type {SanityDocument} from '@sanity/types'\nimport {isValidSignature, SIGNATURE_HEADER_NAME} from '@sanity/webhook'\nimport type {NextRequest} from 'next/server'\n\n/** @public */\nexport type ParsedBody<T> = {\n /**\n * If a secret is given then it returns a boolean. If no secret is provided then no validation is done on the signature, and it'll return `null`\n */\n isValidSignature: boolean | null\n body: T | null\n}\n\n/**\n * Handles parsing the body JSON, and validating its signature. Also waits for Content Lake eventual consistency so you can run your queries\n * without worrying about getting stale data.\n * @public\n */\nexport async function parseBody<Body = SanityDocument>(\n req: NextRequest,\n secret?: string,\n waitForContentLakeEventualConsistency = true,\n): Promise<ParsedBody<Body>> {\n const signature = req.headers.get(SIGNATURE_HEADER_NAME)\n if (!signature) {\n console.error('Missing signature header')\n return {body: null, isValidSignature: null}\n }\n\n const body = await req.text()\n const validSignature = secret ? await isValidSignature(body, signature, secret.trim()) : null\n\n if (validSignature !== false && waitForContentLakeEventualConsistency) {\n await new Promise((resolve) => setTimeout(resolve, 3000))\n }\n\n return {\n body: body.trim() ? JSON.parse(body) : null,\n isValidSignature: validSignature,\n }\n}\n"],"names":[],"mappings":"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","x_google_ignoreList":[0]}