Skip to content

Commit 6ef5e9a

Browse files
authored
docs: Update docs with bodyParser exclusion for webhook signature verification (#1340)
1 parent da8220e commit 6ef5e9a

1 file changed

Lines changed: 34 additions & 25 deletions

File tree

docs/use-cases/event-webhook.md

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
First, follow the guide to [set up signature verification for event webhooks](https://sendgrid.com/docs/for-developers/tracking-events/getting-started-event-webhook-security-features/)
22

3+
**Note:** It is important that the body of the request is verified raw (as a `Buffer` or `string`), not after it was parsed as `json`.
4+
If you are using `express.json()` or `bodyParser.json()` you will need to exclude the webhook path from it (one way to do it is using [express-unless](https://www.npmjs.com/package/express-unless)).
5+
36
An example of a server to process incoming event webhooks:
47
```javascript
5-
const bodyParser = require("body-parser");
8+
const bodyParser = require("body-parser"); // With Express 4.16+ you do not need this and can use express.json() and express.raw() instead
9+
const unless = require('express-unless');
610
const express = require('express');
711
const functions = require("firebase-functions");
812
const app = express();
@@ -15,30 +19,35 @@ const verifyRequest = function (publicKey, payload, signature, timestamp) {
1519
return eventWebhook.verifySignature(ecPublicKey, payload, signature, timestamp);
1620
}
1721

18-
// Using bodyParser middleware to process the body
19-
app.use(bodyParser.text({ type: 'application/json' }));
20-
21-
app.post("/sendgrid/webhook", async (req, resp) => {
22-
try {
23-
const key = '<your_public_key>';
24-
// Alternatively, you can get your key from your firebase function cloud config
25-
// const key = getConfig().sendgrid.webhook_verification_key;
26-
27-
const signature = req.get(EventWebhookHeader.SIGNATURE());
28-
const timestamp = req.get(EventWebhookHeader.TIMESTAMP());
29-
30-
// Be sure to _not_ remove any leading/trailing whitespace characters (e.g., '\r\n').
31-
const requestBody = req.body;
32-
// Alternatively, if using firebase cloud functions, remove the middleware and use:
33-
// const requestBody = (req as functions.https.Request).rawBody;
34-
35-
if (verifyRequest(key, requestBody, signature, timestamp)) {
36-
resp.sendStatus(204);
37-
} else {
38-
resp.sendStatus(403);
22+
// Exclude the webhook path from any json parsing
23+
const json_parser = bodyParser.json();
24+
json_parser.unless = unless;
25+
app.use(json_parser.unless({ path: ["/sendgrid/webhook"]}));
26+
27+
app.post("/sendgrid/webhook",
28+
// parse req.body as a Buffer
29+
bodyParser.raw({ type: 'application/json' }),
30+
async (req, resp) => {
31+
try {
32+
const key = '<your_public_key>';
33+
// Alternatively, you can get your key from your firebase function cloud config
34+
// const key = getConfig().sendgrid.webhook_verification_key;
35+
36+
const signature = req.get(EventWebhookHeader.SIGNATURE());
37+
const timestamp = req.get(EventWebhookHeader.TIMESTAMP());
38+
39+
// Be sure to _not_ remove any leading/trailing whitespace characters (e.g., '\r\n').
40+
const requestBody = req.body;
41+
// Alternatively, if using firebase cloud functions, remove the middleware and use:
42+
// const requestBody = (req as functions.https.Request).rawBody;
43+
44+
if (verifyRequest(key, requestBody, signature, timestamp)) {
45+
resp.sendStatus(204);
46+
} else {
47+
resp.sendStatus(403);
48+
}
49+
} catch (error) {
50+
resp.status(500).send(error);
3951
}
40-
} catch (error) {
41-
resp.status(500).send(error);
42-
}
4352
})
4453
```

0 commit comments

Comments
 (0)