{ "id": "84dT8cFL0FV8ZGPx", "meta": { "instanceId": "workflow-2a221f17", "versionId": "1.0.0", "createdAt": "2025-09-29T07:07:55.472577", "updatedAt": "2025-09-29T07:07:55.472603", "owner": "n8n-user", "license": "MIT", "category": "automation", "status": "active", "priority": "high", "environment": "production" }, "name": "Slack Webhook - Verify Signature", "tags": [ "automation", "n8n", "production-ready", "excellent", "optimized" ], "nodes": [ { "id": "b12fe8e7-45c4-4021-826e-3ae430e34001", "name": "Make Slack Verif Token", "type": "n8n-nodes-base.code", "position": [ 900, 400 ], "parameters": { "jsCode": "function encodeFormData(data) {\n const encodedData = Object.keys(data)\n .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))\n .join('&')\n .replaceAll(\"%20\", \"+\") // Slack does not encode \"+\" signs\n .replaceAll(\"*\", \"%2A\") // Slack encodes \"*\" signs\n .replaceAll(\"~\", \"%7E\"); // Slack encodes \"~\" signs\n \n return encodedData;\n}\n\nfunction buildSigBaseString(requestJson) {\n const version = \"v0\"; // Slack Webhook version (Always v0 for the moment)\n \n const timestamp = requestJson.headers[\"x-slack-request-timestamp\"];\n \n const body = requestJson.body;\n const encodedBody = encodeFormData(body);\n \n const sigBaseString = `${version}:${timestamp}:${encodedBody}`;\n\n return sigBaseString;\n}\n\nconst requestJson = $input.first().json;\n\nconst sigBaseString = buildSigBaseString(requestJson);\n\nconst requestSignature = requestJson.headers[\"x-slack-signature\"];\n\nconsole.log({\n sigBaseString,\n requestSignature\n });\nreturn {\n json: {\n sigBaseString,\n requestSignature\n },\n pairedItem: 0\n}\n\n\n" }, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow." }, { "id": "a91e2d8f-e907-439c-9fd3-cb75e957b059", "name": "Encode Secret String", "type": "n8n-nodes-base.crypto", "position": [ 1120, 400 ], "parameters": { "type": "SHA256", "value": "={{ $json.sigBaseString }}", "action": "hmac", "dataPropertyName": "candidateSignature" }, "typeVersion": 1, "notes": "This crypto node performs automated tasks as part of the workflow." }, { "id": "d79ccfe1-61cd-4da4-bfff-1e504627bb3d", "name": "If Node", "type": "n8n-nodes-base.if", "position": [ 1360, 400 ], "parameters": { "conditions": { "string": [ { "value1": "={{ $json.requestSignature }}", "value2": "=v0={{ $json.candidateSignature }}" } ] } }, "typeVersion": 1, "notes": "This if node performs automated tasks as part of the workflow." }, { "id": "cb2b9908-c226-438b-adb2-7c1ec852e007", "name": "Stop and Error", "type": "n8n-nodes-base.stopAndError", "position": [ 1580, 580 ], "parameters": { "errorMessage": "Could not verify Slack Webhook signature" }, "typeVersion": 1, "notes": "This stopAndError node performs automated tasks as part of the workflow." }, { "id": "5ef4c06a-717b-4f90-83a7-06eda780a892", "name": "Execute Workflow Trigger", "type": "n8n-nodes-base.executeWorkflowTrigger", "position": [ 680, 400 ], "parameters": {}, "typeVersion": 1, "notes": "This executeWorkflowTrigger node performs automated tasks as part of the workflow." }, { "id": "86b022fb-63fd-4ccf-952e-19ed0da54a5c", "name": "Sticky Note", "type": "n8n-nodes-base.stickyNote", "position": [ 880, -420 ], "parameters": { "color": 4, "width": 554.4117841902089, "height": 278.2403290971726, "content": "## Slack Webhook - Verify Signature \nWhen receiving a message from a Slack Webhook, it is much more secure to verify that the message comes from Slack and not from bots or unknown services.\n\nThis small template is designed to validate the received signature (See [this URL]({{ $env.API_BASE_URL }}\n\n### Colors\n- **Blue** areas are **areas to edit**\n- **Yellow** areas are **explanations**" }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "f5af4f44-1ea5-419b-a58b-f8f6839b6b05", "name": "Set Verified to True", "type": "n8n-nodes-base.set", "position": [ 1580, 220 ], "parameters": { "fields": { "values": [ { "name": "signature_verified", "type": "booleanValue" } ] }, "include": "none", "options": {} }, "typeVersion": 3.2, "notes": "This set node performs automated tasks as part of the workflow." }, { "id": "8a76dec8-7a2d-4cc9-82c9-002141e205ec", "name": "Merge", "type": "n8n-nodes-base.merge", "position": [ 1920, 40 ], "parameters": { "mode": "combine", "options": {}, "combinationMode": "mergeByPosition" }, "typeVersion": 2.1, "notes": "This merge node performs automated tasks as part of the workflow." }, { "id": "0c8506bc-b114-4d25-8586-80549ae0026d", "name": "Sticky Note1", "type": "n8n-nodes-base.stickyNote", "position": [ 1000, 40 ], "parameters": { "color": 6, "width": 359.58396920054975, "height": 548.3119898759418, "content": "## TO EDIT \nSet your Slack Signing Secret.\nYou can obtain it by visiting your Slack App dashboard in the general tab: {{ $env.API_BASE_URL }}[SLACK_APP_ID]/general\n" }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "20cca69c-9d00-4471-8845-2cb91458c23e", "name": "Sticky Note2", "type": "n8n-nodes-base.stickyNote", "position": [ 1560, 399 ], "parameters": { "width": 300.4638046519632, "height": 360.20237540316725, "content": "## Error Output\nIf the signature cannot be verified, an error will be raised. You can manage this scenario in your main workflow by either using an Error Workflow or by modifying your node settings and selecting appropriate actions in the event of an error.\n" }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "55ede23b-acb4-43ea-ac32-c678dd48e056", "name": "Sticky Note3", "type": "n8n-nodes-base.stickyNote", "position": [ 1880, -220 ], "parameters": { "width": 300.4638046519632, "height": 427.3843805720155, "content": "## Success Output\nIf the signature is successfully verified, we return a key `verified_signature` set to `true` along with the data from the Slack request itself.\n" }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "22d44888-5af4-43b9-b514-ebfc9c61b07c", "name": "Sticky Note4", "type": "n8n-nodes-base.stickyNote", "position": [ 560, 160 ], "parameters": { "width": 300.4638046519632, "height": 427.3843805720155, "content": "## Input\nThe input should be the received Slack request. Place this workflow directly after the Slack Webhook.\n" }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "error-1b49962c", "name": "Error Handler", "type": "n8n-nodes-base.stopAndError", "typeVersion": 1, "position": [ 1000, 400 ], "parameters": { "message": "Workflow execution error", "options": {} } } ], "active": false, "pinData": {}, "settings": { "executionOrder": "v1", "saveManualExecutions": true, "callerPolicy": "workflowsFromSameOwner", "errorWorkflow": null, "timezone": "UTC", "executionTimeout": 3600, "maxExecutions": 1000, "retryOnFail": true, "retryCount": 3, "retryDelay": 1000 }, "versionId": "f9e78d89-0da8-465e-aa47-5396d9ac4d48", "connections": {}, "description": "Automated workflow: Slack Webhook - Verify Signature. This workflow integrates 8 different services: stickyNote, code, merge, set, stopAndError. It contains 12 nodes and follows best practices for error handling and security.", "notes": "Excellent quality workflow: Slack Webhook - Verify Signature. This workflow has been optimized for production use with comprehensive error handling, security, and documentation." }