In order to validate HMAC and save the body of the webhooks in S3 bucket we put the code below in Lambda.<\/p>\n\n\n
\n\/\/ modules\/lambda\/index.mjs\nimport crypto from "crypto";\nimport { S3Client, PutObjectCommand } from "@aws-sdk\/client-s3";\n\nfunction createHmac(message) {\n const hmac = crypto.createHmac("sha256", process.env.API_SECRET);\n hmac.update(message);\n return hmac.digest("hex");\n}\n\nfunction verifyHmac(message, receivedHmac) {\n const hmacFromMessage = createHmac(message);\n return crypto.timingSafeEqual(\n Buffer.from(hmacFromMessage, "utf8"),\n Buffer.from(receivedHmac, "utf8")\n );\n}\n\nasync function saveToS3(s3client, webhookBody, key) {\n const input = {\n Body: webhookBody,\n Bucket: process.env.BUCKET,\n Key: key,\n };\n const command = new PutObjectCommand(input);\n await s3client.send(command);\n}\n\nexport const handler = async (event) => {\n console.log("Event: ", event);\n const response = {\n batchItemFailures: [],\n };\n\n for (const record of event.Records) {\n console.log("message attributes ", record.messageAttributes);\n const payload = JSON.parse(record.body);\n\n const bodyString = record.body;\n const hmacSignature = record.messageAttributes.hmac.stringValue;\n\n try {\n if (!verifyHmac(bodyString, hmacSignature)) {\n console.log("Invalid HMAC. Dropping webhook");\n continue;\n }\n } catch (error) {\n console.log("Error verifying HMAC. Dropping webhook", error);\n continue;\n }\n\n const client = new S3Client({\n region: process.env.REGION,\n });\n\n try {\n await saveToS3(\n client,\n bodyString,\n record.messageAttributes["webook-id"].stringValue\n );\n console.log("Processing payload webhook", payload);\n } catch (error) {\n console.log("Error processing webhook", error);\n \/\/ failed record will go back to queue\n response.batchItemFailures.push({ itemIdentifier: record.messageId });\n }\n }\n\n return response;\n};\n<\/pre><\/div>\n\n\nIn the code above we create HMAC helper functions to validate HMAC. Next we also create a function to save webhook’s body into S3 bucket. In Lambda handler function we defined a response as an object with a property batchItemsFailure<\/code> that is assigned to an empty array. When setting up SQS as Lambda trigger we selected ReportBatchItemFailures<\/code>. In this case if we get let’s say a batch of 10 webhooks from SQS and 3 fails, we can update batchItemsFaillure<\/code> array to contain the IDs of the events for those webhooks. SQS will know to that only those events failed and will make them visible again. The other 7 events for webhooks will be considered successfully processed an would be delete from the queue.<\/p>\n\n\n\nWe wrap HMAC validation in try catch<\/code> statement and simply drop the webhook if validation fails, because API Gateway already responded with OK 200 when it received the webhook and placed it on the queue. <\/p>\n\n\n\nFinally we attempt to save webhook’s body to the S3 bucket and also create a console log<\/code> statement designating processing of the webhook. If an error happens the catch<\/code> block will log it and update batchItemFailures<\/code> array with a failed event message ID. <\/p>\n\n\n\nAPI Gateway Webhooks Testing<\/h2>\n\n\n\n
To test the setup we use the same approach with Postman’s pre-request script as we used in HMAC Validation<\/a> article. Please, provide x-topic<\/code>, and x-wehbook-id<\/code> headers (x-hmac<\/code> header will be added automatically by the pre-request script). The API Gateway will reject the requests without these heasders. Also don’t forget to add API_SECRET variable to Lambda function’s environment variables that matches the secret used in pre-request script. If everything works, you should see webhooks saved in the S3 bucket.<\/p>\n\n\n\nConclusion<\/h2>\n\n\n\n
In conclusion, our exploration of AWS API Gateway, SQS, Lambda, and HMAC Validation underscores the pivotal role of Amazon Web Services in modern API development. By mastering these core components, we empower ourselves to design scalable, efficient, and secure APIs.<\/p>\n","protected":false},"excerpt":{"rendered":"
In the previous post HMAC Validation we explored how to validate HMAC in an API project. The video Webhooks Processing: HTTP API Gateway + SQS +Lambda shows how to created a scalable solution for receiving webhooks and throttling them with SQS and Lambda. In this article we will look at how to use HMAC Validation…<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"footnotes":""},"categories":[15],"tags":[30,28,27,29,26],"class_list":["post-30619","post","type-post","status-publish","format-standard","hentry","category-javascript","tag-api-development","tag-api-gatewa","tag-aws","tag-lambda","tag-terraform"],"yoast_head":"\n
API Gateway Webhooks Lambda HMAC Validation | Alex Rusin Blog<\/title>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\t\n\t\n