1
import { TriggerClient } from "@trigger.dev/sdk";
2
import { createHmac, timingSafeEqual } from "crypto";
3
import { Slack } from "@trigger.dev/slack";
4
const slack = new Slack({ id: "slack" });
15
const brex = client.defineHttpEndpoint({
19
verify: async (request) => {
20
const webhook_id = request.headers.get("Webhook-Id");
21
const webhook_signature = request.headers.get("Webhook-Signature");
22
const webhook_timestamp = request.headers.get("Webhook-Timestamp");
23
const body = await request.text();
25
if (!webhook_id || !webhook_signature || !webhook_timestamp) {
26
return { success: false, reason: "Missing brex headers" };
29
const signed_content = `${webhook_id}.${webhook_timestamp}.${body}`;
30
const passed_signatures = webhook_signature
32
.map((sigString) => sigString.split(",")[1]);
34
const response = await fetch(
35
`https://platform.brexapis.com/v1/webhooks/secrets`,
38
"Content-Type": "application/json",
39
Authorization: `Bearer ${process.env.BREX_API_KEY}`,
44
const data = await response.json();
45
const secrets = data.map(
46
(secretObj: { secret: string; status: string }) => secretObj.secret
49
for (const secret of secrets) {
50
const base64DecodedSecret = Buffer.from(secret, "base64");
51
const hmac = createHmac("sha256", base64DecodedSecret);
52
const computed_signature = hmac.update(signed_content).digest();
54
for (const passed_signature of passed_signatures) {
55
const decodedPassedSignature = Buffer.from(passed_signature, "base64");
57
if (timingSafeEqual(computed_signature, decodedPassedSignature)) {
58
return { success: true };
63
return { success: false, reason: "Invalid brex signature" };
74
trigger: brex.onRequest(),
78
run: async (request, io, ctx) => {
79
const body = await request.json();
80
await io.logger.info(`Body`, body);
82
switch (body.event_type) {
83
case "USER_UPDATED": {
84
await io.slack.postMessage("user-updated", {
85
channel: process.env.SLACK_CHANNEL!,
86
text: `User updated:\nUser id: ${
88
}\nUpdated attributes: ${body.updated_attributes.join(", ")}`,