Skip to content

Instantly share code, notes, and snippets.

@audiojak
Created May 9, 2024 22:17
Show Gist options
  • Save audiojak/78e06ddfb4f54aea801cb3faf6271637 to your computer and use it in GitHub Desktop.
Save audiojak/78e06ddfb4f54aea801cb3faf6271637 to your computer and use it in GitHub Desktop.
Webhook handler with verification for replicate.com: /app/api/replicate/route.ts
import crypto from "crypto";
export async function POST(request: Request) {
try {
const secret = process.env.REPLICATE_WEBHOOK_TOKEN;
if (!secret) {
return new Response("Unauthorized", {
status: 401,
});
}
// deconstruct the request
const body = await request.text();
const webhook_id = await request.headers.get("Webhook-Id");
const webhook_timestamp = await request.headers.get("Webhook-Timestamp");
const webhookSignatures = await request.headers.get("Webhook-Signature");
if (!webhookSignatures) {
return new Response("Unauthorized", {
status: 401,
});
}
const signedContent = `${webhook_id}.${webhook_timestamp}.${body}`;
// Base64 decode the secret
const secretBytes = await Buffer.from(secret.split("_")[1], "base64");
const signature = crypto
.createHmac("sha256", secretBytes)
.update(signedContent)
.digest("base64");
const expectedSignatures = webhookSignatures
.split(" ")
.map((sig) => sig.split(",")[1]);
const isValid = expectedSignatures.some(
(expectedSignature) => expectedSignature === signature
);
if (!isValid) {
console.log("Webhook not authorized");
return new Response("Unauthorized", {
status: 401,
});
} else {
//do stuff
const jsonData = JSON.parse(body);
console.log("Webhook authorized");
//return response
return new Response("Authorized", {
status: 200,
});
}
} catch (error) {
if (error instanceof Error) {
return new Response(`Webhook error: ${error.message}`, {
status: 400,
});
}
return new Response("Webhook error", {
status: 400,
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment