Unable to verify Signature Sendgrid Event Webhook?
Closed this issue · 2 comments
weijialiu-hiretual commented
Webhook:
@ApiTags('SendGrid Event')
@Controller('/sendgrid-event')
export class SendGridEventController {
constructor(
private readonly appService: AppService,
private readonly sendGridEventService: SendGridEventService,
private readonly logger: Logger,
) {
// For winston info.type
this.logger.setContext('sendgrid-inbound-parse');
}
/**
* Receive the Event Webhook from SendGrid
*
* @return {String} return 200
*/
@Post('/webhook')
eventHandler(
@Req() request: Request,
@Headers('x-twilio-email-event-webhook-signature') signature: string,
@Headers('x-twilio-email-event-webhook-timestamp') timestamp: number,
@Body() body: any,
): any {
console.log(
'--------------------------------------------------------------------------------------------',
);
this.logger.log(' /POST /sendgrid-event/webhook');
this.logger.log(JSON.stringify(body));
const reqHeader = request.rawHeaders.join(',');
// Verify headers.
if (!signature || !timestamp) {
this.logger.error(`Forbidden: ${reqHeader}`);
return {
ok: false,
message: '403, Forbidden',
};
}
// Convert the public key string to a ECPublicKey.
const ECPublicKey = this.sendGridEventService.convertPublicKeyToECDSA(
process.env.SENDGRID_EVENT_PUBLICKEY_FOR_ENGAGEMENT,
);
// Verify the signature.
//! Be sure to include the trailing carriage return and newline! - '\r\n'
const payload = JSON.stringify(body) + '\r\n';
const verifyResult = this.sendGridEventService.verifySignature(
ECPublicKey,
payload,
signature,
timestamp,
);
if (!verifyResult) {
this.logger.error(`Unauthorized: ${reqHeader}`);
return {
ok: false,
message: '401, Unauthorized',
};
}
this.sendGridEventService.handleSendGridEvent(body);
}
}
And the function convertPublicKeyToECDSA
and verifySignature
:
/**
* Convert the public key string to a ECPublicKey.
*
* @param {string} publicKey verification key under Mail Settings
* @return {PublicKey} A public key using the ECDSA(Elliptic Curve Digital Signature Algorithm) algorithm
*/
convertPublicKeyToECDSA(publicKey) {
return PublicKey.fromPem(publicKey);
}
/**
* Verify signed event webhook requests.
*
* @param {PublicKey} publicKey elliptic curve public key
* @param {string|Buffer} payload event payload in the request body
* @param {string} signature value obtained from the 'X-Twilio-Email-Event-Webhook-Signature' header
* @param {string} timestamp value obtained from the 'X-Twilio-Email-Event-Webhook-Timestamp' header
* @return {Boolean} true or false if signature is valid
*/
verifySignature(publicKey, payload, signature, timestamp) {
const timestampPayload = timestamp + payload;
const decodedSignature = Signature.fromBase64(signature);
return Ecdsa.verify(timestampPayload, decodedSignature, publicKey);
}
But the same code, sometime it works, but sometime it doesn't work.
- Send Email to
1234@qq.com
:
request:
{
"personalizations": [
{
"to": [
{
"email": "1234@qq.com",
"name": "Weijia Liu"
}
],
"headers": {
"Message-ID": "<xxx-test-message-id-3@hiretual.com>",
"In-Reply-To": "<xxx-test-message-id-1@hiretual.com>",
"References": "<xxx-test-message-id-1@hiretual.com>"
}
}
],
"from": {
"email": "from-xxx@xxxx.xxx.com",
"name": "from-xxx"
},
"reply_to": {
"email": "random-email-address-1@xx-xxx.testhtm.com ",
"name": "from-xxxx"
},
"subject": "Test SG 1",
"content": [
{
"type": "text/html",
"value": "<p>Hello 3 from Twilio SendGrid!</p><p>%open-track%</p><a href='https://www.google.com'>Click here</a>"
}
],
"tracking_settings": {
"click_tracking": {
"enable": true,
"enable_text": false
},
"open_tracking": {
"enable": true,
"substitution_tag": "%open-track%"
}
}
}
It works in webhook:
--------------------------------------------------------------------------------------------
[2021-12-06T09:43:03.724Z][info] service=standalone-webhooks|type=sendgrid-inbound-parse|content= /POST /sendgrid-event/webhook
[2021-12-06T09:43:03.725Z][info] service=standalone-webhooks|type=sendgrid-inbound-parse|content=[{"email":"1234@qq.com","event":"dropped","reason":"Bounced Address","sg_event_id":"ZHJvcC0yNDIzOTU2OC1XSEhiVzlUOVNsMjcyRFFBT0QwN3B3LTA","sg_message_id":"WHHbW9T9Sl272DQAOD07pw.filterdrecv-75ff7b5ffb-ktk29-1-61ADDB24-A.0","smtp-id":"<WHHbW9T9Sl272DQAOD07pw@geopod-ismtpd-3-0>","timestamp":1638783780}]
- But When I send to
12345@qq.com
request:
{
"personalizations": [
{
"to": [
{
"email": "12345@qq.com",
"name": "Weijia Liu"
}
],
"headers": {
"Message-ID": "<xxx-test-message-id-3@hiretual.com>",
"In-Reply-To": "<xxx-test-message-id-1@hiretual.com>",
"References": "<xxx-test-message-id-1@hiretual.com>"
}
}
],
"from": {
"email": "from-xxx@xxxx.xxx.com",
"name": "from-xxx"
},
"reply_to": {
"email": "random-email-address-1@xx-xxx.testhtm.com ",
"name": "from-xxxx"
},
"subject": "Test SG 1",
"content": [
{
"type": "text/html",
"value": "<p>Hello 3 from Twilio SendGrid!</p><p>%open-track%</p><a href='https://www.google.com'>Click here</a>"
}
],
"tracking_settings": {
"click_tracking": {
"enable": true,
"enable_text": false
},
"open_tracking": {
"enable": true,
"substitution_tag": "%open-track%"
}
}
}
It didn't work:
--------------------------------------------------------------------------------------------
[2021-12-06T09:46:20.898Z][info] service=standalone-webhooks|type=sendgrid-inbound-parse|content= /POST /sendgrid-event/webhook
[2021-12-06T09:46:20.899Z][info] service=standalone-webhooks|type=sendgrid-inbound-parse|content=[{"email":"12345@qq.com","event":"processed","send_at":0,"sg_event_id":"cHJvY2Vzc2VkLTI0MjM5NTY4LWxvb3ZzSXByVFUyZ0Z4aUprSHRHbVEtMA","sg_message_id":"loovsIprTU2gFxiJkHtGmQ.filterdrecv-7bc86b958d-gt6pz-1-61ADDBE3-E.0","smtp-id":"<xiao-test-message-id-3@hiretual.com>","timestamp":1638783971},{"email":"12345@qq.com","event":"delivered","ip":"50.31.49.42","response":"250 OK: queued as.","sg_event_id":"ZGVsaXZlcmVkLTAtMjQyMzk1NjgtbG9vdnNJcHJUVTJnRnhpSmtIdEdtUS0w","sg_message_id":"loovsIprTU2gFxiJkHtGmQ.filterdrecv-7bc86b958d-gt6pz-1-61ADDBE3-E.0","smtp-id":"<xiao-test-message-id-3@hiretual.com>","timestamp":1638783974,"tls":1}]
[2021-12-06T09:46:20.919Z][error] service=standalone-webhooks|type=sendgrid-inbound-parse|content=Unauthorized: Host,0b57-52-8-30-104.ngrok.io,User-Agent,SendGrid Event API,Content-Length,634,Accept-Encoding,gzip,Content-Type,application/json;charset=utf-8,X-Forwarded-For,167.89.119.29,X-Forwarded-Proto,https,X-Twilio-Email-Event-Webhook-Signature,MEUCIQD/rRwu4YRS5euzXaYfJl7gcekDNVSAhoIBoSFjb5BS1gIgG15aET6uZHax6pt8YloWv8spYFNLXqX2CT0t6LHF+mY=,X-Twilio-Email-Event-Webhook-Timestamp,1638783980
shwetha-manvinkurke commented
Looks like your payload is a multi event payload and sendgrid expects a \r\n
after each event. Can you give this a try?
JenniferMah commented
Closing due to inactivity. Please open a new Github issue if you still need help.