Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Update docs with bodyParser exclusion for webhook signature verification #1340

Merged
merged 1 commit into from Feb 25, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
59 changes: 34 additions & 25 deletions docs/use-cases/event-webhook.md
@@ -1,8 +1,12 @@
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/)

**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`.
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)).

An example of a server to process incoming event webhooks:
```javascript
const bodyParser = require("body-parser");
const bodyParser = require("body-parser"); // With Express 4.16+ you do not need this and can use express.json() and express.raw() instead
const unless = require('express-unless');
const express = require('express');
const functions = require("firebase-functions");
const app = express();
Expand All @@ -15,30 +19,35 @@ const verifyRequest = function (publicKey, payload, signature, timestamp) {
return eventWebhook.verifySignature(ecPublicKey, payload, signature, timestamp);
}

// Using bodyParser middleware to process the body
app.use(bodyParser.text({ type: 'application/json' }));

app.post("/sendgrid/webhook", async (req, resp) => {
try {
const key = '<your_public_key>';
// Alternatively, you can get your key from your firebase function cloud config
// const key = getConfig().sendgrid.webhook_verification_key;

const signature = req.get(EventWebhookHeader.SIGNATURE());
const timestamp = req.get(EventWebhookHeader.TIMESTAMP());

// Be sure to _not_ remove any leading/trailing whitespace characters (e.g., '\r\n').
const requestBody = req.body;
// Alternatively, if using firebase cloud functions, remove the middleware and use:
// const requestBody = (req as functions.https.Request).rawBody;

if (verifyRequest(key, requestBody, signature, timestamp)) {
resp.sendStatus(204);
} else {
resp.sendStatus(403);
// Exclude the webhook path from any json parsing
const json_parser = bodyParser.json();
json_parser.unless = unless;
app.use(json_parser.unless({ path: ["/sendgrid/webhook"]}));

app.post("/sendgrid/webhook",
// parse req.body as a Buffer
bodyParser.raw({ type: 'application/json' }),
async (req, resp) => {
try {
const key = '<your_public_key>';
// Alternatively, you can get your key from your firebase function cloud config
// const key = getConfig().sendgrid.webhook_verification_key;

const signature = req.get(EventWebhookHeader.SIGNATURE());
const timestamp = req.get(EventWebhookHeader.TIMESTAMP());

// Be sure to _not_ remove any leading/trailing whitespace characters (e.g., '\r\n').
const requestBody = req.body;
// Alternatively, if using firebase cloud functions, remove the middleware and use:
// const requestBody = (req as functions.https.Request).rawBody;

if (verifyRequest(key, requestBody, signature, timestamp)) {
resp.sendStatus(204);
} else {
resp.sendStatus(403);
}
} catch (error) {
resp.status(500).send(error);
}
} catch (error) {
resp.status(500).send(error);
}
})
```