{
  "id": "JxGoWSeBkNUoBvTc",
  "meta": {
    "templateId": "12062",
    "templateCredsSetupCompleted": true
  },
  "name": "Control AI agent tool access with Port RBAC and Slack mentions",
  "tags": [],
  "nodes": [
    {
      "id": "5c4ceb24-7ccd-4b90-b2d7-14d723d08c74",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        2448,
        896
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o",
          "cachedResultName": "gpt-4o"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "132461b5-bd6f-4e68-b623-eaea4c536e3c",
      "name": "Check permissions",
      "type": "@n8n/n8n-nodes-langchain.code",
      "notes": "A tool to check user's allowed tools and permissions",
      "position": [
        2880,
        880
      ],
      "parameters": {
        "code": {
          "supplyData": {
            "code": "const { DynamicTool } = require(\"@langchain/core/tools\");\nconst connectedTools = await this.getInputConnectionData('ai_tool', 0);\nconst allowedTools = $input.item.json.allowed_tools;\n\nconst noTool = (tool) => {\n  return new DynamicTool({\n    name: tool.getName(),\n    description: tool.description,\n    func: async () => {\n        return \"Tell the user 'You are not authorized to use this tool'.\";\n    },\n  });\n}\n\nreturn connectedTools.map(connectedTool => {\n  const permissionGranted = allowedTools.includes(connectedTool.getName());\n  return permissionGranted ? connectedTool : noTool(connectedTool);\n});"
          }
        },
        "inputs": {
          "input": [
            {
              "type": "ai_tool",
              "required": true
            }
          ]
        },
        "outputs": {
          "output": [
            {
              "type": "ai_tool"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "0a041ea8-f282-4120-8c4e-5e86df4373d2",
      "name": "Set input",
      "type": "n8n-nodes-base.set",
      "position": [
        2576,
        624
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "9ea62c8f-984b-4c05-8e40-549d8035c4d3",
              "name": "name",
              "type": "string",
              "value": "={{ $json.entity.identifier }}"
            },
            {
              "id": "bf74b2c4-f0d1-458a-9044-5cb1b62722e6",
              "name": "granted_roles",
              "type": "array",
              "value": "={{ $json.entity.relations.roles || [] }}"
            },
            {
              "id": "e0f4d3d7-a916-43cb-a13d-e4453b0d1a3b",
              "name": "allowed_tools",
              "type": "array",
              "value": "={{ $json.entity.properties.allowed_tools || [] }}"
            },
            {
              "id": "26d0f442-4cd3-4930-a80b-fc471226dd36",
              "name": "regions",
              "type": "array",
              "value": "={{ $('Get Regions from Port').item.json.entities.map(e => e.identifier) }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "5e15aaad-cf19-401b-a96a-97b9d028d177",
      "name": "calculator",
      "type": "@n8n/n8n-nodes-langchain.toolCalculator",
      "position": [
        2928,
        1136
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "a5c4e743-3894-45c7-a3b9-fbf42ac3eed1",
      "name": "Unknown user",
      "type": "n8n-nodes-base.if",
      "position": [
        2224,
        496
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1d042f5b-ef39-4b9e-8d9c-900b39dbe3fb",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              },
              "leftValue": "={{ $json.ok }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "966a41d6-68b0-4629-84f3-0d925b6e88e3",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2800,
        832
      ],
      "parameters": {
        "color": 7,
        "width": 380,
        "height": 220,
        "content": "Uses list of allowed tools gathered from Port to check for permissions and replaces denied tools with a fixed instruction to return a message to the user."
      },
      "typeVersion": 1
    },
    {
      "id": "9394b13a-262f-49fa-9b3e-99f1a17766a3",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2736,
        560
      ],
      "parameters": {
        "color": 7,
        "width": 380,
        "height": 240,
        "content": "AI agent with the instruction to always use the connected tools to respond to the user's request"
      },
      "typeVersion": 1
    },
    {
      "id": "b9cf7ecf-5372-4e75-be15-eebbd79a458f",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2512,
        560
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "Collects input and formats it using required keys"
      },
      "typeVersion": 1
    },
    {
      "id": "f307100d-d57d-42a8-bc1f-d93a6cc62a82",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1120,
        416
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "Listens to messages directly sent to the Slack bot"
      },
      "typeVersion": 1
    },
    {
      "id": "1be0d592-609b-45b6-9970-ad7d49f43e96",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1952,
        432
      ],
      "parameters": {
        "color": 7,
        "width": 428,
        "height": 240,
        "content": "Checks if the user was found in Port"
      },
      "typeVersion": 1
    },
    {
      "id": "e1f41863-12e5-4d35-8d6c-2f14884fb4ad",
      "name": "Slack Trigger",
      "type": "n8n-nodes-base.slackTrigger",
      "position": [
        1168,
        496
      ],
      "parameters": {
        "options": {},
        "trigger": [
          "app_mention"
        ],
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "channelId"
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "0204fb6e-05b1-46c4-8d09-d7b8a8666b01",
      "name": "Send a message",
      "type": "n8n-nodes-base.slack",
      "position": [
        2576,
        384
      ],
      "parameters": {
        "text": "User not found in Port. Please contact your administrator.",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Slack Trigger').item.json.channel }}"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "2b119903-8698-4d05-b6ed-d50ed3f40fd0",
      "name": "Get user permission from Port",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2016,
        496
      ],
      "parameters": {
        "url": "=https://api.port.io/v1/blueprints/_user/entities/{{ $('Get user\\'s slack profile').item.json.email }}",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ $('Get Port access token').item.json.tokenType }} {{ $('Get Port access token').item.json.accessToken }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "76183718-358b-443d-af04-4b7cef4130b7",
      "name": "Wikipedia",
      "type": "@n8n/n8n-nodes-langchain.toolWikipedia",
      "position": [
        3056,
        1152
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "4f99e983-0ca3-4e79-8e55-bf4745531bf0",
      "name": "Create an incident in PagerDuty",
      "type": "n8n-nodes-base.pagerDutyTool",
      "position": [
        3248,
        1136
      ],
      "parameters": {
        "email": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Email', ``, 'string') }}",
        "title": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Title', ``, 'string') }}",
        "resource": "incident",
        "operation": "create",
        "serviceId": "YOUR_PD_SERVICE_ID",
        "authentication": "apiToken",
        "descriptionType": "auto",
        "additionalFields": {},
        "conferenceBridgeUi": {}
      },
      "credentials": {
        "pagerDutyApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "60a35b13-fa61-4b86-91e6-3e0b849b930f",
      "name": "Create a bucket in AWS S3",
      "type": "n8n-nodes-base.awsS3Tool",
      "position": [
        2768,
        1136
      ],
      "parameters": {
        "name": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('BucketName', ``, 'string') }}",
        "resource": "bucket",
        "additionalFields": {
          "region": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Region', ``, 'string') }}"
        }
      },
      "credentials": {
        "aws": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "2a538a0c-3539-4511-8ea9-63002a6e7c2a",
      "name": "Get user's slack profile",
      "type": "n8n-nodes-base.slack",
      "position": [
        1392,
        496
      ],
      "parameters": {
        "user": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.user }}"
        },
        "resource": "user",
        "operation": "getProfile"
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "50b8dda9-26c5-41ac-8f47-a88f688d8b81",
      "name": "Get Port access token",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1600,
        496
      ],
      "parameters": {
        "url": "https://api.port.io/v1/auth/access_token",
        "method": "POST",
        "options": {},
        "jsonBody": "{\n  \"clientId\": \"REPLACE WITH CLIENT ID\",\n  \"clientSecret\": \"REPLACE WITH CLIENT SECRET\"\n}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4.3
    },
    {
      "id": "bcad96f3-3279-465c-8378-08656dd71e88",
      "name": "Chat Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        2640,
        896
      ],
      "parameters": {
        "sessionKey": "={{ $json.name}}",
        "sessionIdType": "customKey"
      },
      "typeVersion": 1.3
    },
    {
      "id": "74000745-d221-4b0c-97fa-25b1f9c1a0f8",
      "name": "Send output message",
      "type": "n8n-nodes-base.slack",
      "position": [
        3184,
        624
      ],
      "parameters": {
        "text": "={{ $json.output }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Slack Trigger').item.json.channel }}"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "fffead23-643d-48ef-9e17-ca894ee75967",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        368,
        240
      ],
      "parameters": {
        "width": 688,
        "height": 576,
        "content": "## AI Agent Access Control (Port + Slack)\n\nThis workflow adds role-based access control to AI agents. Users @mention the bot in Slack, and the workflow checks their permissions in Port before letting the agent use any tools.\n\n### How it works\n1. Slack trigger picks up @mentions and gets the user's email.\n2. Authenticates with Port and looks up the user in the _user blueprint.\n3. If the user exists, reads their allowed_tools array.\n4. The LangChain code node filters tools at runtime, swapping any unauthorized tool with a \"not authorized\" stub.\n5. AI agent runs with only permitted tools, then posts the response back to Slack.\n\n### Setup\n- [ ] Connect your Slack account and set the channel ID.\n- [ ] Add your OpenAI API key.\n- [ ] Get a free Port account at port.io.\n- [ ] Create the [rbac blueprints](https://docs.port.io/guides/all/implement-rbac-for-ai-agents-with-n8n-and-port/#set-up-the-port-data-model) in Port with an allowed_tools property (string array).\n- [ ] Add user entities with their email as identifier and allowed tools listed.\n- [ ] Replace the Port client ID and secret in the \"Get Port access token\" node.\n- [ ] Connect any tool credentials you want to use (PagerDuty, AWS, etc.).\n- [ ] Invite the bot to your Slack channel."
      },
      "typeVersion": 1
    },
    {
      "id": "a5396cc8-81f7-454d-9e0f-fe5bf9d87652",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2800,
        624
      ],
      "parameters": {
        "text": "={{ $('Slack Trigger').item.json.text }}",
        "options": {
          "systemMessage": "=You are a personal assistant. The name of the current user is \"{{ $json.name }}\"\nYou MUST only use the provided tools to process any user input. Never use general knowledge to answer questions. If you can't use a tool, tell the user why.\n\nBelow are the list of allowed tools for this user:\n{{ $json.allowed_tools }}\n\nRegions available to use when interacting with AWS: {{ $json.regions }}",
          "returnIntermediateSteps": true
        },
        "promptType": "define"
      },
      "typeVersion": 1.8
    },
    {
      "id": "076909ce-1054-41ef-8cf7-a3d85a56c493",
      "name": "Get Regions from Port",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1792,
        496
      ],
      "parameters": {
        "url": "https://api.port.io/v1/blueprints/region/entities",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ $('Get Port access token').item.json.tokenType }} {{ $('Get Port access token').item.json.accessToken }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "0cd92b77-2f77-46e0-aa63-5424cc642de2",
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Send output message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set input": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wikipedia": {
      "ai_tool": [
        [
          {
            "node": "Check permissions",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "calculator": {
      "ai_tool": [
        [
          {
            "node": "Check permissions",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Chat Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Unknown user": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack Trigger": {
      "main": [
        [
          {
            "node": "Get user's slack profile",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check permissions": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Get Port access token": {
      "main": [
        [
          {
            "node": "Get Regions from Port",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Regions from Port": {
      "main": [
        [
          {
            "node": "Get user permission from Port",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get user's slack profile": {
      "main": [
        [
          {
            "node": "Get Port access token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create a bucket in AWS S3": {
      "ai_tool": [
        [
          {
            "node": "Check permissions",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Get user permission from Port": {
      "main": [
        [
          {
            "node": "Unknown user",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create an incident in PagerDuty": {
      "ai_tool": [
        [
          {
            "node": "Check permissions",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    }
  }
}