diff --git a/components/http/actions/verify-hmac-signature/verify-hmac-signature.mjs b/components/http/actions/verify-hmac-signature/verify-hmac-signature.mjs new file mode 100644 index 0000000000000..0600bfa70b1e0 --- /dev/null +++ b/components/http/actions/verify-hmac-signature/verify-hmac-signature.mjs @@ -0,0 +1,68 @@ +import crypto from "crypto"; + +export default { + name: "Verify HMAC Signature", + version: "0.0.1", + key: "http-verify-hmac-signature", + description: "Validate HMAC signature for incoming HTTP webhook requests. Make sure to configure the HTTP trigger to \"Return a custom response from your workflow\".", + type: "action", + props: { + http: "$.interface.http", + secret: { + type: "string", + label: "Secret", + description: "Your secret key used for validation", + secret: true, + }, + signature: { + type: "string", + label: "Signature", + description: "The HMAC signature received from the incoming webhook, typically provided in a specific HTTP header", + }, + bodyRaw: { + type: "string", + label: "Raw Body", + description: "The raw request body received from the webhook caller, provided as a string without any parsing or modifications", + }, + customResponse: { + type: "boolean", + label: "Return Error to Webhook Caller", + description: "If `True`, returns a `401: Invalid credentials` error in the case of invalid authorization. **Make sure to configure the HTTP trigger to \"Return a custom response from your workflow\"**. If `False`, does not return a custom response in the case of invalid auth.", + default: true, + }, + }, + methods: { + _checkHmac(secret, signature, bodyRaw) { + const expectedSignature = crypto + .createHmac("sha256", secret) + .update(bodyRaw, "utf8") + .digest(); + + const signatureBuffer = Buffer.from(signature, "hex"); + if (signatureBuffer.length !== expectedSignature.length) { + return false; + } + return crypto.timingSafeEqual(signatureBuffer, expectedSignature); + }, + }, + async run({ $ }) { + const valid = this._checkHmac( + this.secret, + this.signature, + this.bodyRaw, + ); + + if (!valid) { + if (this.customResponse) { + await $.respond({ + status: 401, + headers: {}, + body: "Invalid credentials", + }); + } + return $.flow.exit("Invalid credentials"); + } + + $.export("$summary", "HTTP request successfully authenticated"); + }, +}; diff --git a/components/http/package.json b/components/http/package.json index 781985f1a78ff..65291c52efda4 100644 --- a/components/http/package.json +++ b/components/http/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/http", - "version": "0.4.1", + "version": "0.5.0", "description": "Pipedream Http Components", "main": "http.app.js", "keywords": [