The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "hmac-verify",
"name": "hmac-verify",
"nodes": [
{
"parameters": {},
"id": "start",
"name": "On Sub-Workflow Call",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1,
"position": [
240,
300
]
},
{
"parameters": {
"jsCode": "// HMAC-SHA256 Verification (R-003, R-014)\n// Centralized sub-workflow \u2014 called by all webhook workflows\n// Constitution II: Threat-model driven (unauthorized webhook callers)\n// 019: Reads secret from own Static Data (not $env) for N8N_BLOCK_ENV_ACCESS_IN_NODE=true\n\nconst crypto = require('crypto');\n\n// Read HMAC secret from this workflow's Static Data\nconst staticData = $getWorkflowStaticData('global');\nconst secret = staticData.webhookSecret;\nif (!secret) {\n return [{ json: { verified: false, error: 'HMAC secret not configured \u2014 set webhookSecret in hmac-verify Static Data' } }];\n}\n\n// Read headers and body from the calling workflow's input\nconst inputData = $input.all()[0].json;\nconst signature = inputData.headers?.['x-signature'] || '';\nconst timestamp = inputData.headers?.['x-timestamp'] || '';\nconst rawBody = inputData.rawBody || JSON.stringify(inputData.body || {});\n\n// Replay protection: timestamp must be within 5 minutes\nconst now = Math.floor(Date.now() / 1000);\nconst requestTime = parseInt(timestamp, 10);\nif (isNaN(requestTime) || Math.abs(now - requestTime) > 300) {\n return [{ json: { verified: false, error: 'Timestamp expired or invalid (>5 min drift)' } }];\n}\n\n// Compute expected signature\nconst expectedSig = 'sha256=' + crypto\n .createHmac('sha256', secret)\n .update(rawBody, 'utf8')\n .digest('hex');\n\n// Constant-time comparison\nconst sigBuffer = Buffer.from(signature);\nconst expectedBuffer = Buffer.from(expectedSig);\n\nif (sigBuffer.length !== expectedBuffer.length || !crypto.timingSafeEqual(sigBuffer, expectedBuffer)) {\n return [{ json: { verified: false, error: 'HMAC signature mismatch' } }];\n}\n\nreturn [{ json: { verified: true, body: inputData.body } }];"
},
"id": "hmac-code",
"name": "Verify HMAC",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
460,
300
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "76f4d3e9-e1c2-4711-963b-b095d2311ba4",
"leftValue": "={{ $json.verified }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "true"
}
}
],
"combinator": "and"
}
},
"id": "check-result",
"name": "Is Verified?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
680,
300
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ error: 'Unauthorized', details: $json.error }) }}",
"options": {
"responseCode": 401
}
},
"id": "reject",
"name": "Reject 401",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
900,
420
],
"notes": "Returns 401 for failed HMAC verification"
}
],
"connections": {
"On Sub-Workflow Call": {
"main": [
[
{
"node": "Verify HMAC",
"type": "main",
"index": 0
}
]
]
},
"Verify HMAC": {
"main": [
[
{
"node": "Is Verified?",
"type": "main",
"index": 0
}
]
]
},
"Is Verified?": {
"main": [
[],
[
{
"node": "Reject 401",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"meta": {
"notes": "HMAC-SHA256 verification sub-workflow. Called by all webhook workflows as first step. R-014: Single source of truth for verification logic. Constitution II: Proves caller identity + request integrity + replay protection."
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
hmac-verify. Uses executeWorkflowTrigger. Event-driven trigger; 4 nodes.
Source: https://github.com/traylorre/openclaw-mac/blob/d5ef2bd70e230b1877d7165d35c2e27947b269e0/workflows/hmac-verify.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Agendamiento. Uses n8n-nodes-evolution-api, redis, dataTable, executeWorkflowTrigger. Event-driven trigger; 60 nodes.
Prevent concurrent workflow runs using Redis. Uses executeWorkflowTrigger, manualTrigger, stickyNote, executeWorkflow. Event-driven trigger; 43 nodes.
This workflow sets a small "lock" value in Redis so that only one copy of a long job can run at the same time. If another trigger fires while the job is still busy, the workflow sees the lock, stops e
Reputation Engine — Site Refresh. Uses httpRequest, executeWorkflowTrigger. Event-driven trigger; 35 nodes.
Using n8n a lot?