{
  "name": "n8nCal \u00b7 /auth",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "n8ncal/auth",
        "responseMode": "responseNode",
        "options": {
          "allowedOrigins": "*"
        }
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        -700,
        0
      ],
      "id": "b0881203-a45d-46e1-bbde-731579f6d629",
      "name": "Webhook \u00b7 /auth"
    },
    {
      "parameters": {
        "jsCode": "// Validate passphrase against env. Constant-time compare to dodge timing attacks.\nconst body = $input.first().json.body || {};\nconst submitted = String(body.passphrase || '');\nconst expected = String($env.N8NCAL_PASSPHRASE || '');\n\nif (!expected) {\n  throw new Error('N8NCAL_PASSPHRASE not configured on this n8n instance');\n}\n\nconst { timingSafeEqual } = require('crypto');\nconst a = Buffer.from(submitted.padEnd(expected.length, '\\0'));\nconst b = Buffer.from(expected.padEnd(submitted.length, '\\0'));\nconst ok = a.length === b.length && timingSafeEqual(a, b) && submitted.length === expected.length;\n\nif (!ok) {\n  throw new Error('invalid passphrase');\n}\n\nconst now = Math.floor(Date.now() / 1000);\nreturn [{\n  json: {\n    sub: body.device || 'web',\n    tenant_id: body.tenant_id || 'default',\n    iat: now,\n    exp: now + 24 * 60 * 60\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -480,
        0
      ],
      "id": "73afb3d4-bf61-439e-b916-41b0c8281b16",
      "name": "Validate passphrase",
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "operation": "sign",
        "useJson": true,
        "claimsJson": "={{ JSON.stringify($json) }}",
        "options": {
          "algorithm": "HS256"
        }
      },
      "type": "n8n-nodes-base.jwt",
      "typeVersion": 1,
      "position": [
        -260,
        0
      ],
      "id": "e3813f95-4787-4544-ba91-b567958767ce",
      "name": "Sign JWT",
      "credentials": {
        "jwtAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "t",
              "name": "token",
              "value": "={{ $json.token }}",
              "type": "string"
            },
            {
              "id": "tt",
              "name": "token_type",
              "value": "Bearer",
              "type": "string"
            },
            {
              "id": "ex",
              "name": "expires_at",
              "value": "={{ DateTime.now().plus({ hours: 24 }).toISO() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -40,
        0
      ],
      "id": "d16f74a9-2bb1-4f33-b2e3-958177dfb22f",
      "name": "Shape response"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ $json }}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        180,
        0
      ],
      "id": "fb387e9c-ddb3-4494-bc6b-3aeec47f092c",
      "name": "Respond"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n  \"error\": \"invalid_passphrase\"\n}",
        "options": {
          "responseCode": 401
        }
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        -260,
        200
      ],
      "id": "77466dca-60b6-4d9e-95a8-5af129bd20fc",
      "name": "Respond 401"
    }
  ],
  "connections": {
    "Webhook \u00b7 /auth": {
      "main": [
        [
          {
            "node": "Validate passphrase",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate passphrase": {
      "main": [
        [
          {
            "node": "Sign JWT",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond 401",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sign JWT": {
      "main": [
        [
          {
            "node": "Shape response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Shape response": {
      "main": [
        [
          {
            "node": "Respond",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "tags": [
    {
      "name": "n8nCal"
    }
  ]
}