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