Skip to content

Commit d0968a6

Browse files
Fix: storyblok webhook signature (#412)
* fix signature * fix: tests for webhooks --------- Co-authored-by: Ellie Re'em <[email protected]>
1 parent 44c11f1 commit d0968a6

File tree

3 files changed

+23
-11
lines changed

3 files changed

+23
-11
lines changed

src/webhooks/webhooks.controller.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Body, Controller, Headers, Logger, Post, UseGuards } from '@nestjs/common';
1+
import { Body, Controller, Headers, Logger, Post, Request, UseGuards } from '@nestjs/common';
22
import { ApiBody, ApiTags } from '@nestjs/swagger';
33
import { EventLogEntity } from 'src/entities/event-log.entity';
44
import { TherapySessionEntity } from 'src/entities/therapy-session.entity';
@@ -46,8 +46,8 @@ export class WebhooksController {
4646

4747
@Post('storyblok')
4848
@ApiBody({ type: StoryDto })
49-
async updateStory(@Body() data: StoryDto, @Headers() headers) {
49+
async updateStory(@Request() req, @Body() data: StoryDto, @Headers() headers) {
5050
const signature: string | undefined = headers['webhook-signature'];
51-
return this.webhooksService.updateStory(data, signature);
51+
return this.webhooksService.updateStory(req, data, signature);
5252
}
5353
}

src/webhooks/webhooks.service.spec.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,14 @@ import { WebhooksService } from './webhooks.service';
5454
const webhookSecret = process.env.STORYBLOK_WEBHOOK_SECRET;
5555

5656
const getWebhookSignature = (body) => {
57-
return createHmac('sha1', webhookSecret).update(JSON.stringify(body)).digest('hex');
57+
return createHmac('sha1', webhookSecret).update(""+body).digest('hex');
5858
};
59+
const createRequestObject = (body) => {
60+
return {
61+
rawBody: "" + body,
62+
setEncoding: ()=>{},
63+
encoding: "utf8"
64+
}}
5965

6066
// Difficult to mock classes as well as node modules.
6167
// This seemed the best approach
@@ -228,7 +234,7 @@ describe('WebhooksService', () => {
228234
text: '',
229235
};
230236

231-
return expect(service.updateStory(body, getWebhookSignature(body))).rejects.toThrow(
237+
return expect(service.updateStory(createRequestObject(body), body, getWebhookSignature(body))).rejects.toThrow(
232238
'STORYBLOK STORY NOT FOUND',
233239
);
234240
});
@@ -241,6 +247,7 @@ describe('WebhooksService', () => {
241247
};
242248

243249
const deletedStory = (await service.updateStory(
250+
createRequestObject(body),
244251
body,
245252
getWebhookSignature(body),
246253
)) as SessionEntity;
@@ -256,6 +263,7 @@ describe('WebhooksService', () => {
256263
};
257264

258265
const unpublished = (await service.updateStory(
266+
createRequestObject(body),
259267
body,
260268
getWebhookSignature(body),
261269
)) as SessionEntity;
@@ -306,7 +314,7 @@ describe('WebhooksService', () => {
306314
text: '',
307315
};
308316

309-
const session = (await service.updateStory(body, getWebhookSignature(body))) as SessionEntity;
317+
const session = (await service.updateStory(createRequestObject(body), body, getWebhookSignature(body))) as SessionEntity;
310318

311319
expect(courseFindOneSpy).toHaveBeenCalledWith({
312320
storyblokUuid: 'anotherCourseUuId',
@@ -349,7 +357,7 @@ describe('WebhooksService', () => {
349357
text: '',
350358
};
351359

352-
const session = (await service.updateStory(body, getWebhookSignature(body))) as SessionEntity;
360+
const session = (await service.updateStory(createRequestObject(body), body, getWebhookSignature(body))) as SessionEntity;
353361

354362
expect(session).toEqual(mockSession);
355363
expect(courseFindOneSpy).toHaveBeenCalledWith({
@@ -408,7 +416,7 @@ describe('WebhooksService', () => {
408416
text: '',
409417
};
410418

411-
const session = (await service.updateStory(body, getWebhookSignature(body))) as SessionEntity;
419+
const session = (await service.updateStory(createRequestObject(body), body, getWebhookSignature(body))) as SessionEntity;
412420

413421
expect(session).toEqual(mockSession);
414422
expect(sessionSaveRepoSpy).toHaveBeenCalledWith({
@@ -442,7 +450,7 @@ describe('WebhooksService', () => {
442450
text: '',
443451
};
444452

445-
const course = (await service.updateStory(body, getWebhookSignature(body))) as CourseEntity;
453+
const course = (await service.updateStory(createRequestObject(body), body, getWebhookSignature(body))) as CourseEntity;
446454

447455
expect(course).toEqual(mockCourse);
448456
expect(courseFindOneRepoSpy).toHaveBeenCalledWith({

src/webhooks/webhooks.service.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ export class WebhooksService {
534534
}
535535
}
536536

537-
async updateStory(data: StoryDto, signature: string | undefined) {
537+
async updateStory(req, data: StoryDto, signature: string | undefined) {
538538
// Verify storyblok signature uses storyblok webhook secret - see https://www.storyblok.com/docs/guide/in-depth/webhooks#securing-a-webhook
539539
if (!signature) {
540540
const error = `Storyblok webhook error - no signature provided`;
@@ -543,7 +543,11 @@ export class WebhooksService {
543543
}
544544

545545
const webhookSecret = process.env.STORYBLOK_WEBHOOK_SECRET;
546-
const bodyHmac = createHmac('sha1', webhookSecret).update(JSON.stringify(data)).digest('hex');
546+
547+
req.rawBody = '' + data;
548+
req.setEncoding('utf8');
549+
550+
const bodyHmac = createHmac('sha1', webhookSecret).update(req.rawBody).digest('hex');
547551

548552
if (bodyHmac !== signature) {
549553
const error = `Storyblok webhook error - signature mismatch`;

0 commit comments

Comments
 (0)