{
  "id": "cIhoaw0VJGihOHmy",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Authentication Panel (Signup/Login/Forgot Password)",
  "tags": [],
  "nodes": [
    {
      "id": "e430c384-eec7-4c18-a105-ae8d4d5216bc",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        176,
        1568
      ],
      "parameters": {
        "color": 5,
        "width": 600,
        "height": 640,
        "content": "## Overview: Authentication\n**Purpose:** Manages user signup, login, and password reset via a single webhook.\n**Features:**\n- **Signup:** Creates new users with bcrypt-hashed passwords.\n- **Login:** Verifies credentials case-insensitively.\n- **Forgot Password:** Generates and returns a new random password.\n**Prerequisites:**\n- PostgreSQL/Supabase with `users` table.\n- Enable `uuid-ossp` and `pgcrypto` extensions.\n- Set PostgreSQL credentials in n8n.\n**Usage:** POST to `/webhook/auth` with:\n```json\n{ \"path\": \"signup|signin|forgot\", \"email\": \"string\", \"password\": \"string\", \"name\": \"string\" }\n```\n**Example:** `{\"path\": \"signup\", \"email\": \"user@example.com\", \"password\": \"pass123\", \"name\": \"John Doe\"}`\n**Output:** `{\"status\": \"success|error\", \"message\": \"string\", \"data\": {}}`"
      },
      "typeVersion": 1
    },
    {
      "id": "a81f1cc2-4d7b-4d71-a103-667f43724c17",
      "name": "Webhook Doc",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        816,
        1568
      ],
      "parameters": {
        "color": 5,
        "width": 624,
        "height": 464,
        "content": "## Webhook: Entry Point\n**Purpose:** Handles all auth requests via POST.\n**Endpoint:** `/webhook/auth`\n**Input JSON:**\n```json\n{ \"path\": \"signup|signin|forgot\", \"email\": \"string\", \"password\": \"string\", \"name\": \"string\" }\n```\n**Notes:**\n- `path` routes to signup, login, or forgot password.\n- Only POST method supported."
      },
      "typeVersion": 1
    },
    {
      "id": "ee0f4193-74e8-4df4-ba1e-09950cc38dfe",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        1104,
        1856
      ],
      "parameters": {
        "path": "auth",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "1a42c7bc-b3ff-4495-8207-cffe9c2edc70",
      "name": "Switch Doc",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1456,
        1568
      ],
      "parameters": {
        "color": 5,
        "width": 528,
        "height": 472,
        "content": "## Switch: Route Actions\n**Purpose:** Routes requests based on `path`.\n**Rules:**\n- `signup` \u2192 Signup\n- `signin` \u2192 Login\n- `forgot` \u2192 Forgot Password\n**Default:** No action for invalid `path`.\n**Notes:** Ensure `path` is lowercase."
      },
      "typeVersion": 1
    },
    {
      "id": "e4b6610a-5fcc-4dea-82d7-b8388ba39cdc",
      "name": "Router",
      "type": "n8n-nodes-base.switch",
      "position": [
        1648,
        1824
      ],
      "parameters": {
        "rules": {
          "rules": [
            {
              "output": 1,
              "value2": "signup"
            },
            {
              "output": 2,
              "value2": "signin"
            },
            {
              "output": 3,
              "value2": "forgot"
            }
          ]
        },
        "value1": "={{$json[\"path\"]}}",
        "dataType": "string",
        "fallbackOutput": 0
      },
      "typeVersion": 1
    },
    {
      "id": "68549d4c-294a-4a74-a849-ee87814334fb",
      "name": "Signup Doc",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2000,
        1568
      ],
      "parameters": {
        "color": 6,
        "width": 580,
        "height": 472,
        "content": "## PostgreSQL: Signup\n**Purpose:** Creates a new user.\n**SQL:**\n```sql\nINSERT INTO users (full_name, email, password_hash)\nVALUES ('{{$json[\"name\"]}}', '{{$json[\"email\"]}}', crypt('{{$json[\"password\"]}}', gen_salt('bf')))\nRETURNING id, full_name, email, created_at;\n```\n**Output:** `{ id, full_name, email, created_at }`\n**Notes:**\n- Uses bcrypt for password hashing.\n- Requires unique email in `users` table."
      },
      "typeVersion": 1
    },
    {
      "id": "2dd10f62-a7af-4ed3-bbf9-acb3ab00a5c7",
      "name": "Signup",
      "type": "n8n-nodes-base.postgres",
      "position": [
        2352,
        1888
      ],
      "parameters": {
        "query": "INSERT INTO users (full_name, email, password_hash)\nVALUES (\n  '{{$json[\"name\"]}}',\n  '{{$json[\"email\"]}}',\n  crypt('{{$json[\"password\"]}}', gen_salt('bf'))\n)\nRETURNING id, full_name, email, created_at;\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "2ded10d4-9715-4a06-991a-b5224039f5aa",
      "name": "Login Doc",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        832,
        2096
      ],
      "parameters": {
        "color": 6,
        "width": 532,
        "height": 600,
        "content": "## PostgreSQL: Login\n**Purpose:** Verifies user credentials.\n**SQL:**\n```sql\nSELECT\n  id,\n  full_name,\n  email,\n  (password_hash = crypt('{{$json[\"password\"]}}', password_hash)) AS \"isPasswordMatch\"\nFROM users\nWHERE LOWER(email) = LOWER('{{$json[\"email\"]}}');\n```\n**Output:** `{ id, full_name, email, isPasswordMatch }`\n**Notes:**\n- Case-insensitive email check.\n- `isPasswordMatch` confirms correct password."
      },
      "typeVersion": 1
    },
    {
      "id": "7cb04861-2a0b-4298-b050-f6fd232439e1",
      "name": "Login",
      "type": "n8n-nodes-base.postgres",
      "position": [
        864,
        2528
      ],
      "parameters": {
        "query": "SELECT\n  id,\n  full_name,\n  email,\n  (password_hash = crypt('{{$json[\"password\"]}}', password_hash)) AS \"isPasswordMatch\"\nFROM users\nWHERE LOWER(email) = LOWER('{{$json[\"email\"]}}');\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "d189c3bf-9e95-4666-b205-50734ee47821",
      "name": "IF Doc",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1392,
        2096
      ],
      "parameters": {
        "color": 6,
        "width": 400,
        "height": 600,
        "content": "## IF: Validate Login\n**Purpose:** Checks if login is valid.\n**Condition:** `id` exists and `isPasswordMatch` is `true`.\n**Outputs:**\n- True \u2192 Success response.\n- False \u2192 Error response (invalid credentials).\n**Notes:** Ensures only valid users proceed."
      },
      "typeVersion": 1
    },
    {
      "id": "b154b3cb-d21e-4451-b526-fca72281d0ab",
      "name": "Check Login",
      "type": "n8n-nodes-base.if",
      "position": [
        1504,
        2528
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "41d2a73b-ece5-4043-894a-ab8bf12e55b3",
              "operator": {
                "type": "boolean",
                "operation": "equal"
              },
              "leftValue": "={{ $json[\"id\"] !== undefined && $json[\"isPasswordMatch\"] === true }}",
              "rightValue": "true"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f7b96eac-f2a1-489c-92e5-10ab62244003",
      "name": "Forgot Password Doc",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1808,
        2096
      ],
      "parameters": {
        "color": 6,
        "width": 580,
        "height": 608,
        "content": "## PostgreSQL: Forgot Password\n**Purpose:** Resets password to a random 8-char string.\n**SQL:**\n```sql\nWITH new_pass AS (\n  SELECT substring(md5(random()::text) from 1 for 8) AS plain_password\n)\nUPDATE users\nSET password_hash = crypt(new_pass.plain_password, gen_salt('bf'))\nFROM new_pass\nWHERE LOWER(email) = LOWER('{{$json[\"email\"]}}')\nRETURNING email, new_pass.plain_password AS newPassword;\n```\n**Output:** `{ email, newPassword }`\n**Notes:**\n- Case-insensitive email.\n- New password can be emailed."
      },
      "typeVersion": 1
    },
    {
      "id": "360ce51c-db91-46ec-841e-518abb068712",
      "name": "Reset Password",
      "type": "n8n-nodes-base.postgres",
      "position": [
        2144,
        2416
      ],
      "parameters": {
        "query": "WITH new_pass AS (\n  SELECT substring(md5(random()::text) from 1 for 8) AS plain_password\n)\nUPDATE users\nSET password_hash = crypt(new_pass.plain_password, gen_salt('bf'))\nFROM new_pass\nWHERE LOWER(email) = LOWER('{{$json[\"email\"]}}')\nRETURNING email, new_pass.plain_password AS newPassword;\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "0a9bf2d6-a7ff-47e4-874f-b6ad0274199c",
      "name": "Response Doc",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2432,
        2096
      ],
      "parameters": {
        "color": 5,
        "width": 688,
        "height": 600,
        "content": "## Respond: Send Response\n**Purpose:** Returns JSON response.\n**Format:**\n```json\n{\n  \"status\": \"success|error\",\n  \"message\": \"string\",\n  \"data\": {}\n}\n```\n**Notes:**\n- Success: Returns query results.\n- Error: Handles invalid inputs or failures."
      },
      "typeVersion": 1
    },
    {
      "id": "cef94eb3-936a-496a-a950-90c8708ec56a",
      "name": "Respond",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2848,
        2544
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1.4
    },
    {
      "id": "b5d535cc-a1a0-4674-b56f-4f9f2fd6a08d",
      "name": "Database Setup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        176,
        2240
      ],
      "parameters": {
        "color": 5,
        "width": 600,
        "height": 480,
        "content": "## Database Setup\n**Purpose:** Configure PostgreSQL/Supabase.\n**Steps:**\n1. Create Supabase project at [supabase.com](https://supabase.com).\n2. Enable extensions: `uuid-ossp`, `pgcrypto`.\n3. Create `users` table:\n```sql\nCREATE TABLE users (\n  id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),\n  full_name text NOT NULL,\n  email text UNIQUE NOT NULL,\n  password_hash text NOT NULL,\n  created_at timestamptz DEFAULT now()\n);\n```\n4. Add PostgreSQL credentials in n8n.\n**Notes:** Ensure unique email constraint."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "e51c5384-1056-4f65-a0f1-187b68971e0e",
  "connections": {
    "Login": {
      "main": [
        [
          {
            "node": "Check Login",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Router": {
      "main": [
        [],
        [
          {
            "node": "Signup",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Login",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Reset Password",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Signup": {
      "main": [
        [
          {
            "node": "Respond",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Router",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Login": {
      "main": [
        [
          {
            "node": "Respond",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reset Password": {
      "main": [
        [
          {
            "node": "Respond",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}