This workflow corresponds to n8n.io template #10040 — we link there as the canonical source.
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": "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
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
postgres
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Streamline user onboarding and security for your applications using this n8n workflow. This template handles signup, login, and password resets through a single endpoint, making it ideal for developers building MVPs or scaling apps without a full authentication backend.
Source: https://n8n.io/workflows/10040/ — 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.
Scraping. Uses httpRequest, postgres, @apify/n8n-nodes-apify, respondToWebhook. Webhook trigger; 61 nodes.
Workflow B — AI Listing Engine. Uses httpRequest, postgres, errorTrigger. Webhook trigger; 47 nodes.
This workflow automates data maturity evaluation to measure how well an organization uses data to create value by capturing assessment data through forms or APIs, processing and scoring responses usin