I'm attempting to set up an outgoing webhook for Microsoft Teams to build a basic Bot using callin.io.
To confirm that a webhook request genuinely originated from Microsoft servers, it includes an HMAC signature of the request body within the Authorization header. Verifying this signature should be straightforward using the Crypto module, however, the hashes are not matching.
It appears I need to encrypt the raw request body as it's received by Node.js.
I tried enabling the Raw Body option and using {{ $node["Webhook"].binary }}
in my Crypto node, but this only results in the following error:
TypeError [ERR_INVALID_ARG_TYPE]: The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received an instance of Object
at Hmac.update (internal/crypto/hash.js:84:11)
at Object.execute (/usr/local/lib/node_modules/n8n/node_modules/n8n-nodes-base/dist/nodes/Crypto.node.js:364:66)
at Workflow.runNode (/usr/local/lib/node_modules/n8n/node_modules/n8n-workflow/dist/src/Workflow.js:508:37)
at /usr/local/lib/node_modules/n8n/node_modules/n8n-core/dist/src/WorkflowExecute.js:442:62
at processTicksAndRejections (internal/process/task_queues.js:93:5)
Here's the complete crypto node for your reference:
{
"parameters": {
"action": "hmac",
"type": "SHA256",
"value": "={{$node["Webhook"].binary}}",
"dataPropertyName": "signature",
"secret": "redacted",
"encoding": "base64"
},
"name": "Calculate Signature",
"type": "n8n-nodes-base.crypto",
"typeVersion": 1,
"position": [
850,
450
]
}
Hey,
Have you attempted to convert the binary data into JSON format using the Read Binary node? This approach might resolve your problem.
Hi,
I wasn't aware that was required, but I'll give it a shot! I'm just curious which node you're referring to – I only see "Read binary file," which needs a file path, and "Move binary data," which I'm not sure about. Thanks for your assistance!
Hey,
My apologies, it's the Move Binary Data node. Within the node, you can choose the Binary to JSON mode to convert the binary data into a JSON object. You can then reference this in your subsequent node.
Hello,
thanks. I've added the node as requested, and I can see the new property with the JSON string, but the signatures still don't align. I have a basic node sample from Microsoft that demonstrates signature calculation works in this scenario:
const crypto = require('crypto');
const sharedSecret = "base64-encoded-secret-here"; // e.g. "+ZaRRMC8+mpnfGaGsBOmkIFt98bttL5YQRq3p2tXgcE="
const bufSecret = Buffer(sharedSecret, "base64");
var http = require('http');
http.createServer(function(request, response) {
var payload = '';
request.on('data', function (data) {
payload += data;
});
request.on('end', function() {
var auth = this.headers['authorization'];
var msgBuf = Buffer.from(payload, 'utf8');
var msgHash = "HMAC " + crypto.createHmac('sha256', bufSecret).update(msgBuf).digest("base64");
console.log("Computed HMAC: " + msgHash);
console.log("Received HMAC: " + auth);
// ...
This is almost identical to what the crypto module does, but the node sample computes a correct signature, while callin.io's crypto module does not.
Any insights?
Hi,
You can utilize the sim
node within the Function node. Ensure that crypto
is exported and available in the node. If you are using the callin.io cloud, you won't need to export it. For self-hosted callin.io instances, you can export crypto by following the guide provided here: [HELP] Not been able to import external modules - #8 by jan
Do you have any sample data that I can use to test the Crypto node?
Hi,
I just figured it out… the key is the secret decoding:
const bufSecret = Buffer(sharedSecret, "base64");
This creates a buffer from the base64-encoded binary secret. It needs to be passed as binary data; otherwise, decoding won't work. So, to make it work, I used the following function (in case someone else encounters this):
for (let item of items) {
item.json.secret = Buffer.from(item.json.rawSecret, 'base64');
}
return items;
Perhaps a trigger node for Teams’ incoming webhooks that handled all of this would be beneficial?
Anyway, thanks a lot
That's great!
I've found a related feature request. Could you share your specific use case here and give it your vote?
(If your scenario is different, please feel free to create a new Feature Request
)