From cd2ea9236ca1a30d718a81a828456b6ce452eab1 Mon Sep 17 00:00:00 2001 From: Jared Piedt Date: Thu, 19 Jun 2025 14:05:01 -0400 Subject: [PATCH 1/3] feat(backend): Add event_attributes to Webhook type --- .changeset/sixty-regions-camp.md | 5 +++++ packages/backend/src/api/resources/Webhooks.ts | 9 ++++++++- packages/backend/src/webhooks.ts | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 .changeset/sixty-regions-camp.md diff --git a/.changeset/sixty-regions-camp.md b/.changeset/sixty-regions-camp.md new file mode 100644 index 00000000000..dfaea5b8673 --- /dev/null +++ b/.changeset/sixty-regions-camp.md @@ -0,0 +1,5 @@ +--- +'@clerk/backend': patch +--- + +Add `event_attributes` to `Webhook`. diff --git a/packages/backend/src/api/resources/Webhooks.ts b/packages/backend/src/api/resources/Webhooks.ts index 5cec4379e3a..aba2cbe33f5 100644 --- a/packages/backend/src/api/resources/Webhooks.ts +++ b/packages/backend/src/api/resources/Webhooks.ts @@ -13,7 +13,14 @@ import type { WaitlistEntryJSON, } from './JSON'; -type Webhook = { type: EvtType; object: 'event'; data: Data }; +type WebhookEventAttributes = { + http_request: { + client_ip: string; + user_agent: string; + }; +}; + +type Webhook = { type: EvtType; object: 'event'; data: Data; event_attributes: WebhookEventAttributes }; export type UserWebhookEvent = | Webhook<'user.created' | 'user.updated', UserJSON> diff --git a/packages/backend/src/webhooks.ts b/packages/backend/src/webhooks.ts index 273477dae50..ebf115cd696 100644 --- a/packages/backend/src/webhooks.ts +++ b/packages/backend/src/webhooks.ts @@ -90,5 +90,6 @@ export async function verifyWebhook(request: Request, options: VerifyWebhookOpti type: payload.type, object: 'event', data: payload.data, + event_attributes: payload.event_attributes, } as WebhookEvent; } From eadbcbfbdec915b2b79b9c195215d467a21d8e67 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Wed, 9 Jul 2025 08:39:41 -0500 Subject: [PATCH 2/3] Apply suggestion from @brkalow --- .changeset/sixty-regions-camp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/sixty-regions-camp.md b/.changeset/sixty-regions-camp.md index dfaea5b8673..5058f6d0c62 100644 --- a/.changeset/sixty-regions-camp.md +++ b/.changeset/sixty-regions-camp.md @@ -2,4 +2,4 @@ '@clerk/backend': patch --- -Add `event_attributes` to `Webhook`. +Add `event_attributes` to the `Webhook` type. From 728110a0ef8461e5880930290d86478161087d4f Mon Sep 17 00:00:00 2001 From: Jared Piedt Date: Wed, 9 Jul 2025 13:55:01 -0400 Subject: [PATCH 3/3] add test case --- .../backend/src/__tests__/webhooks.test.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/backend/src/__tests__/webhooks.test.ts b/packages/backend/src/__tests__/webhooks.test.ts index 7eb2ee15367..b1ce678a927 100644 --- a/packages/backend/src/__tests__/webhooks.test.ts +++ b/packages/backend/src/__tests__/webhooks.test.ts @@ -217,4 +217,35 @@ describe('verifyWebhook', () => { // All should be treated as missing await expect(verifyWebhook(mockRequest)).rejects.toThrow('svix-id, svix-timestamp, svix-signature'); }); + + it('should parse event_attributes', async () => { + const clerkPayload = JSON.stringify({ + type: 'user.created', + data: { id: 'user_123', email: 'test@example.com' }, + event_attributes: { + http_request: { + client_ip: '127.0.0.1', + user_agent: 'Mozilla/5.0 (Test)', + }, + }, + }); + const svixId = 'msg_123'; + const svixTimestamp = (Date.now() / 1000).toString(); + const validSignature = createValidSignature(svixId, svixTimestamp, clerkPayload); + + const mockRequest = new Request('https://clerk.com/webhooks', { + method: 'POST', + body: clerkPayload, + headers: new Headers({ + 'svix-id': svixId, + 'svix-timestamp': svixTimestamp, + 'svix-signature': validSignature, + }), + }); + + const result = await verifyWebhook(mockRequest, { signingSecret: mockSecret }); + expect(result).toHaveProperty('type', 'user.created'); + expect(result).toHaveProperty('event_attributes.http_request.client_ip', '127.0.0.1'); + expect(result).toHaveProperty('event_attributes.http_request.user_agent', 'Mozilla/5.0 (Test)'); + }); });