AutomationFlowsAI & RAG › Oauth Token Management System with Airtable Storage

Oauth Token Management System with Airtable Storage

ByNazmy @islamnazmi on n8n.io

This workflow contains community nodes that are only compatible with the self-hosted version of n8n.

Webhook trigger★★★★☆ complexity20 nodesAirtableHTTP Request
AI & RAG Trigger: Webhook Nodes: 20 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #6253 — we link there as the canonical source.

This workflow follows the Airtable → HTTP Request recipe pattern — see all workflows that pair these two integrations.

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 →

Download .json
{
  "nodes": [
    {
      "id": "1529173e-b4ae-4352-a378-a327c39e7fed",
      "name": "success",
      "type": "n8n-nodes-base.if",
      "position": [
        448,
        -48
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "faa449f2-815c-41fa-8378-e93093ad10d7",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.success }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "140b3700-3721-4e41-b87c-8df70e995bc4",
      "name": "validation failed",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        672,
        48
      ],
      "parameters": {
        "options": {
          "responseCode": 401
        },
        "respondWith": "json",
        "responseBody": "={\n  \"success\": false,\n  \"error\": \"{{ $('validator').first().json.reason }}\"\n}"
      },
      "typeVersion": 1.4
    },
    {
      "id": "4715c17e-56d8-49f5-bcce-7f5b6f822f49",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        592,
        -304
      ],
      "parameters": {
        "color": 6,
        "width": 260,
        "height": 308,
        "content": "## Database Example\nClone this [Airtable Base](https://airtable.com/appbw5TEhn8xIxxXR/shrN8ve4dfJIXjcAm)"
      },
      "typeVersion": 1
    },
    {
      "id": "722d92c2-445c-4c0a-9e06-a58dbc075907",
      "name": "secret validation",
      "type": "n8n-nodes-base.if",
      "position": [
        1120,
        -240
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "2e4151fc-f513-4a1d-8e42-c77857de22bc",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.secret }}",
              "rightValue": "={{ $('client receiver').item.json.body.client_secret }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a02e3055-eb51-4fec-a429-d8349e388fe4",
      "name": "invalid client",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1120,
        -48
      ],
      "parameters": {
        "options": {
          "responseCode": 400
        },
        "respondWith": "json",
        "responseBody": "{\n\"success\": false,\n\"error\": \"Invalid client id\"\n}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "729d6799-6f74-457c-aac5-ca651e10f170",
      "name": "invalid secret",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1344,
        -144
      ],
      "parameters": {
        "options": {
          "responseCode": 401
        },
        "respondWith": "json",
        "responseBody": "{\n\"success\": false,\n\"error\": \"Invalid client secret\"\n}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "b94ab331-23c2-473d-afdc-e591b89b38e0",
      "name": "generate token",
      "type": "n8n-nodes-base.code",
      "position": [
        1344,
        -336
      ],
      "parameters": {
        "jsCode": "function generateLongToken(length) {\n  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n  let token = '';\n  for (let i = 0; i < length; i++) {\n    token += chars.charAt(Math.floor(Math.random() * chars.length));\n  }\n  return token;\n}\n\nconst token = generateLongToken(32);\n\nreturn [\n  {\n    json: {\n      token\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "204e04af-34a7-4824-b107-4cc3e89199e3",
      "name": "client exists",
      "type": "n8n-nodes-base.if",
      "position": [
        896,
        -144
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "6f16470a-aae4-4647-850e-332284b00a9f",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.id }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "e366461a-ece5-47d1-b5de-ea29aa1846ca",
      "name": "validator",
      "type": "n8n-nodes-base.code",
      "position": [
        224,
        -48
      ],
      "parameters": {
        "jsCode": "const input = $json;\n\nconst body = input.body || {};\n\n// Allowed keys\nconst allowedKeys = ['client_id', 'client_secret'];\n\n// Check required keys\nfor (const key of allowedKeys) {\n  if (!body.hasOwnProperty(key)) {\n    return [{ json: { success: false, reason: `Missing '${key}' in body` } }];\n  }\n}\n\n// Check for extra keys\nconst extraKeys = Object.keys(body).filter(k => !allowedKeys.includes(k));\nif (extraKeys.length > 0) {\n  return [{ json: { success: false, reason: `Body must contain only 'client_id' and 'client_secret', found extra key(s): ${extraKeys.join(', ')}` } }];\n}\n\n// All good\nreturn [{ json: { success: true } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "b7ea9e5f-d097-4ff7-b165-ee1560bb054d",
      "name": "client receiver",
      "type": "n8n-nodes-base.webhook",
      "position": [
        0,
        -48
      ],
      "parameters": {
        "path": "token-refresher",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "70e09e1b-336a-4756-af23-aa4a0636e854",
      "name": "get client id",
      "type": "n8n-nodes-base.airtable",
      "position": [
        672,
        -144
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appbw5TEhn8xIxxXR",
          "cachedResultUrl": "https://airtable.com/appbw5TEhn8xIxxXR",
          "cachedResultName": "Testing Bearer YOUR_TOKEN_HERE "
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tblK2jv4hLOsaKM3m",
          "cachedResultUrl": "https://airtable.com/appbw5TEhn8xIxxXR/tblK2jv4hLOsaKM3m",
          "cachedResultName": "Client IDs"
        },
        "options": {},
        "operation": "search",
        "filterByFormula": "={client} = \"{{ $('client receiver').item.json.body.client_id }}\""
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1,
      "alwaysOutputData": true
    },
    {
      "id": "d5c9d779-5805-45a0-affd-a683a17391ac",
      "name": "create token",
      "type": "n8n-nodes-base.airtable",
      "position": [
        1568,
        -336
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appbw5TEhn8xIxxXR",
          "cachedResultUrl": "https://airtable.com/appbw5TEhn8xIxxXR",
          "cachedResultName": "Testing Bearer YOUR_TOKEN_HERE "
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tblnqvjl4U2t9OMQD",
          "cachedResultUrl": "https://airtable.com/appbw5TEhn8xIxxXR/tblnqvjl4U2t9OMQD",
          "cachedResultName": "Tokens"
        },
        "columns": {
          "value": {
            "Token ID": "={{ $json.token }}",
            "Client IDs": "={{ [$('client receiver').item.json.body.client_id] }}",
            "Token Type": "Bearer",
            "Creation Date": "={{ $now }}"
          },
          "schema": [
            {
              "id": "Token ID",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Token ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Token Type",
              "type": "options",
              "display": true,
              "options": [
                {
                  "name": "Bearer",
                  "value": "Bearer"
                }
              ],
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Token Type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Expiration Date",
              "type": "dateTime",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Expiration Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Issued To",
              "type": "array",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Issued To",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Creation Date",
              "type": "dateTime",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Creation Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Is Active",
              "type": "boolean",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Is Active",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Days Until Expiration",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "Days Until Expiration",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Client IDs",
              "type": "array",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Client IDs",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "typecast": true
        },
        "operation": "create"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1,
      "alwaysOutputData": true
    },
    {
      "id": "09da5fce-3635-4125-903b-a55ca93a5b4b",
      "name": "respond ",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1792,
        -336
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={\n    \"access_token\": \"{{ $('generate token').item.json.token }}\",\n    \"expires_in\": 3600,\n    \"token_type\": \"{{ $json.fields['Token Type'] }}\"\n}"
      },
      "typeVersion": 1.2
    },
    {
      "id": "325443c4-31b4-472f-ab2c-e606776466ba",
      "name": "Other methods",
      "type": "n8n-nodes-base.webhook",
      "position": [
        0,
        272
      ],
      "parameters": {
        "path": "test-jobs",
        "options": {},
        "httpMethod": [
          "DELETE",
          "HEAD",
          "PATCH",
          "PUT",
          "GET"
        ],
        "responseMode": "responseNode",
        "multipleMethods": true
      },
      "typeVersion": 2
    },
    {
      "id": "8b407e94-1e92-4882-872f-f6b2e6e60673",
      "name": "405 Error",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        208,
        320
      ],
      "parameters": {
        "options": {
          "responseCode": 405
        },
        "respondWith": "json",
        "responseBody": "{\n  \"error\": \"Use POST request instead\"\n}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "7bc3accc-791d-4f8a-8f14-80f2d3d4ddf9",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        16,
        -448
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "d94ffe0e-95f7-459c-a837-a08b9ac3d87f",
      "name": "Make a request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        240,
        -448
      ],
      "parameters": {
        "url": "https://localhost:8080/webhook/token-refresher",
        "method": "POST",
        "options": {},
        "jsonBody": "{\n    \"client_id\": \"client_a_1234567890abcdef\",\n    \"client_secret\": \"secret_a_abcdef1234567890\"\n\n}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4.2
    },
    {
      "id": "b9ea6991-ba49-49f0-bffb-5cc807eedb94",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -48,
        -512
      ],
      "parameters": {
        "color": 7,
        "width": 460,
        "height": 220,
        "content": "## Test the request"
      },
      "typeVersion": 1
    },
    {
      "id": "e8814551-20d1-4114-b691-0a45cbf61086",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -48,
        208
      ],
      "parameters": {
        "color": 7,
        "width": 460,
        "height": 280,
        "content": "## HTTP Method handler"
      },
      "typeVersion": 1
    },
    {
      "id": "db9c1b3f-00ba-40fe-8e9c-f900be065940",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -688,
        -512
      ],
      "parameters": {
        "color": 4,
        "width": 580,
        "height": 1656,
        "content": "## OAuth Token Generator and Validator\n\nThis **n8n template** helps you generate, validate, and store tokens for your customers securely using:\n\n- **n8n** as your backend automation engine  \n- **Airtable** as your lightweight client and token store\n\n---\n\n### \ud83d\ude80 What It Does\n\n- Accepts `client_id` and `client_secret` via **POST webhook**.\n- Validates client credentials against Airtable.\n- Generates a **long token** on success.\n- Stores the generated token in Airtable with metadata.\n- Responds with a JSON containing the token, expiry, and type.\n- Returns clear error messages if validation fails.\n\n---\n\n### How It Works\n\n1. **Webhook node** receives `client_id` and `client_secret`.\n2. **Validator (Code node)** checks:\n   - Body contains only `client_id` and `client_secret`.\n   - Rejects missing or extra fields.\n3. **Airtable search**:\n   - Looks up the `client_id`.\n   - Rejects if not found.\n4. **Secret validation (If node)**:\n   - Compares provided `client_secret` with stored value.\n   - Rejects if incorrect.\n5. **Token generation (Code node)**:\n   - Generates a 128-character secure token.\n6. **Airtable create**:\n   - Stores token, client ID, creation date, and type.\n7. **Webhook response**:\n   - Returns JSON `{ access_token, expires_in, token_type }` on success.\n   - Returns appropriate JSON error messages on failure.\n\n---\n\n### Related Workflow\n\nYou can also use it with the published **Bearer YOUR_TOKEN_HERE Validation** workflow:\n\n\ud83d\udc49 [Validate API Requests with Bearer YOUR_TOKEN_HERE Authentication and Airtable](https://n8n.io/workflows/6184-validate-api-requests-with-bearer-token-authentication-and-airtable)\n\nto securely validate tokens you generate with this workflow across your protected endpoints.\n\n---\n\n### Why Use This\n\n- Provides **OAuth-like flows** without a complex backend.\n- Uses **n8n + Airtable** for client management and token storage.\n- Clean, modular, and ready for your SaaS or internal API automations.\n- Extendable for token expiry, refresh, and rotation handling.\n\n---\n\nEnjoy building secure token-based APIs using **n8n + Airtable**! \ud83d\ude80\n\n### Built by:\n[Nazmy](https://n8n.io/creators/islamnazmi/)\n"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "success": {
      "main": [
        [
          {
            "node": "get client id",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "validation failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "validator": {
      "main": [
        [
          {
            "node": "success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "create token": {
      "main": [
        [
          {
            "node": "respond ",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Other methods": {
      "main": [
        [
          {
            "node": "405 Error",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "405 Error",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "405 Error",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "405 Error",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "405 Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "client exists": {
      "main": [
        [
          {
            "node": "secret validation",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "invalid client",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "get client id": {
      "main": [
        [
          {
            "node": "client exists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "generate token": {
      "main": [
        [
          {
            "node": "create token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "client receiver": {
      "main": [
        [
          {
            "node": "validator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "secret validation": {
      "main": [
        [
          {
            "node": "generate token",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "invalid secret",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Make a request",
            "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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow contains community nodes that are only compatible with the self-hosted version of n8n.

Source: https://n8n.io/workflows/6253/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

This powerful n8n automation workflow is designed to execute advanced B2B lead enrichment and hyper-personalization for cold email outreach. By orchestrating a complex chain of data scraping, AI analy

OpenAI, HTTP Request, Airtable
AI & RAG

This template is perfect for e-commerce entrepreneurs, marketers, agencies, and creative teams who want to turn simple product photos and short descriptions into professional flyers or product videos—

Airtable, OpenAI, HTTP Request +1
AI & RAG

This workflow automates the process of generating stylized product photos for e-commerce by combining real product shots with creative templates. It enables the creation of a complete set of images fo

Airtable, HTTP Request, Google Gemini
AI & RAG

This n8n template demonstrates how to capture inbound leads from a form, qualify them with OpenAI, and route the hottest ones to a Bland AI voice agent that calls them back, books a meeting on Google

Airtable, OpenAI, SendGrid +2
AI & RAG

AI-Powered Fake Review Detection Workflow Using n8n & Airtable. Uses httpRequest, airtable, openAi, slack. Webhook trigger; 27 nodes.

HTTP Request, Airtable, OpenAI +1