Fine-Grained Access Control for n8n Workflows

- Share:





2938 Members
n8n is a workflow automation platform that allows you to connect APIs, databases, and services to automate operations. With hundreds of integrations and support for both visual workflows and custom code, organizations use n8n to build everything from simple data syncs to complex multi-step AI agents. You can run it on your own infrastructure or use n8n's cloud-hosted solution.
As you build more complex workflows and handle sensitive operations, authorization becomes important. Who can approve a high-value transaction? Which team members can access customer data? Can a contractor trigger a production deployment? These questions require runtime authorization checks during workflow execution, not just platform-level access control.
Without proper fine-grained authorization, you risk unauthorized access and inconsistent security across your workflow automation.
n8n has built-in access control for managing workflows and credentials at the platform level. With built-in role-based access control, you can assign users as Project Admins, Editors, or Viewers. This determines who can create, edit, execute, or view workflows and credentials in your n8n instance.
But when you're building workflows that need runtime authorization, deciding which users can perform specific actions or access certain resources while the workflow executes, there's no built-in solution.
Developers either skip authorization entirely or build custom solutions from scratch. Some use community templates that combine database lookups with IF nodes, but these require connecting external databases like Airtable, writing custom permission logic in Function nodes, and manually configuring the same pattern for every workflow. Each implementation differs, making it impossible to maintain consistent policies across your automation.
The Permit.io community node (@permitio/n8n-nodes-permitio) brings fine-grained authorization to n8n workflows. Instead of building custom permission logic, you define your policies once in Permit, then drop the Permit node into any n8n workflow to check permissions at runtime.
The node provides three operations:
It supports RBAC, ABAC, and ReBAC policies with automatic attribute extraction from your workflow data.
Unlike custom solutions that require separate database lookups and Function nodes for each workflow, the Permit node handles authorization in a single step. All policies are centralized in the Permit.io system, when you update a user's role, change permission rules, or modify resource access, the changes apply instantly across every n8n workflow using the Permit node. No workflow modifications or redeployment is required.
We'll create an expense approval workflow that uses attribute-based access control (ABAC) to route expense requests to the right approvers. The system processes expense submissions and determines approval paths based on user attributes (department, job level, spending limits) and expense characteristics (amount, category, urgency).
Sorry, your browser doesn't support embedded videos.
Try the Permit.io Policy Editor
Authorization Policies:
The workflow enforces these access control rules:
category, urgency, and the submitter's departmentn8n Workflow:

This approach keeps authorization logic centralized in Permit.io, making policies reusable across workflows while maintaining security and compliance.
To follow along with this tutorial, you should have:
If you haven’t already, create a free Permit.io account at https://app.permit.io.
When you sign in for the first time, Permit will guide you through creating your workspace. A workspace is the home for all your authorization projects. Learn more here.
Once your workspace is created, go to the Projects page and create a new project. Name it something like “Expense Approval System”.

Every project starts with two environments by default:

In this tutorial, we’ll use the Production environment to configure the policy, define attributes, and build the approval logic before connecting it to n8n.

You can use any environment to follow along and setup your policy.
We'll build the expense approval system in four steps:
The first step in any authorization implementation is planning. Before configuring policies in Permit.io, we need to define who can do what, and under which conditions.
Our expense system has four types of users and three permission levels. Here's how they map out:
| User Type | Job Level | Can Do |
| Regular Employees | Junior, Senior | Submit expenses within their spending limit |
| Department Managers | Manager | Approve expenses from their department (up to their approval limit) |
| Senior Managers | Senior Manager | Approve high-value expenses ($5,000+) |
| Finance Team | Any level in Finance dept | Approve and view any expense |
Each user needs these attributes for the policy to work:
| Attribute | Type | Description |
spending_limit |
number | Maximum amount the user can submit |
department |
string | Which department they belong to |
job_level |
string | Their position level (e.g. Junior, Senior, Manager, Senior Manager) |
approval_limit |
number | Maximum amount the user can approve |
Every expense submission includes these details:
| Attribute | Type | Description |
expense_amount |
number | Dollar amount of the expense |
category |
string | Type of expense (travel, meals, supplies, etc.) |
submitter_department |
string | Department of the person who submitted the expense |
urgency |
string | Priority level: normal or urgent |
Here's what each user type is allowed to do:
Regular Employees can submit expenses only if the expense amount is within their spending limit.
Department Managers can approve expenses only if:
Senior Managers can approve high-value expenses ($5,000+) only if the expense amount is within their approval limit.
Finance Team can approve and view any expense regardless of amount or department.
View Access: Users can view only the expenses they're allowed to interact with (submit or approve).
With the policy model defined, we can now move into the next step, where we’ll configure these attributes, resources, and rules directly in the Permit.io Dashboard.
In this step, we’ll configure the authorization schema directly in the Permit.io Dashboard. This includes creating user attributes, defining the expense resource, setting up user sets and resource sets, and creating the rules that connect them.
We’ll start by creating the user attributes that our ABAC rules depend on.
Our ABAC rules rely on user metadata such as spending_limit, department, and job_level. We’ll add these attributes in the Permit.io Dashboard.


You’ll see a form where you can define the attribute name, type, and description.
Create the first attribute:
spending_limitnumber
Click Create Attribute.
Repeat the same process for the remaining attributes:

After creating these four attributes, your user attribute schema is ready for the ABAC rules we’ll build next.
There are 3 built-in attributes —
key, androles— which cannot be edited or deleted.
expense ResourceNext, we’ll define the main object in our approval system, the expense resource. The expense resource represents an expense request. Users will:

submit, approve, view| Attribute | Type |
category |
String |
expense_amount |
Number |
submitter_department |
String |
urgency |
String |

expense resource.

Next, we’ll define User Sets, which group users dynamically based on their attributes. These groups allow Permit.io to evaluate who is allowed to perform actions like submit, approve, or view an expense.

**Name**
Finance Team
**Key**
finance_team
**Condition**
user.department equals Finance

Click Save Dynamic Role.

Repeat for the Remaining User Sets. Create the following additional user sets using the same steps:
| User Set | Key | Conditions |
| Regular Employees | regular_employees | user.job_level equals Junior OR user.job_level equals Senior |
| Department Managers | department_managers | user.job_level equals Manager AND user.approval_limit greater-than-equals resource.expense_amount AND user.department equals resource.submitter_department |
| Senior Managers | senior_managers | user.job_level equals Senior Manager AND user.approval_limit greater-than-equals resource.expense_amount |
Tip: Use Add Condition or Add Condition Group when combining multiple conditions.
Once all four user sets are created, Permit.io can now dynamically group users according to their attributes, making it possible to apply ABAC rules in the next step.
Resource Sets allow you to group expenses based on their attributes. These groups are what your ABAC rules will evaluate when deciding who can act on which expense.
We’ll create the first one together — Department Expenses, and then you’ll repeat the same process for the remaining three resource sets.

**Name**
Department Expenses
**Key**
department_expenses
**Resource Type**
Expense
**Conditions**
- `resource.expense_amount` **equals** `5000`
- `resource.urgency` **equals** `normal`

Click Save Condition.
Repeat for the Remaining Resource Sets
Use the table below to create the remaining three sets:
| Name | Key | Conditions |
| Submittable Expenses | submittable_expenses | resource.expense_amount less-than-equals user.spending_limit |
| High Value Expenses | high_value_expenses | resource.expense_amount greater-than-equals 5000 |
| All Expenses | all_expenses | resource.expense_amount greater-than 0 |
Create each one by clicking Create New again and filling in the corresponding values.

With all user sets and resource sets created, the final step is to connect them by defining who can perform which actions on each resource group. This is done in the Policy Editor.
Allow employees to submit and view only expenses within their spending limit.
| Resource Set | submit | approve | view |
| Submittable Expenses | ✅ | — | ✅ |
---
Allow managers to approve and view expenses from their department under $5,000.
| Resource Set | submit | approve | view |
| Department Expenses | — | ✅ | ✅ |
---
Allow senior managers to handle high–value expenses.
| Resource Set | submit | approve | view |
| High Value Expenses | — | ✅ | ✅ |
---
Finance can see and approve everything.
| Resource Set | submit | approve | view |
| All Expenses | — | ✅ | ✅ |
Sorry, your browser doesn't support embedded videos.
Once all checkboxes follow the mapping above, your ABAC policy is fully configured in the Dashboard.
After defining the schema in Permit.io, you'll the add actual users with their attribute values in the Permit.io Directory.
Navigate to the Directory section in your Permit.io dashboard and click "Add user" in the top right corner.

Fill out the user creation form with these details:
Key: taofeek2sure@gmail.com Email: taofeek2sure@gmail.com First Name: Taofiq Last Name: Taofiq Tenant: Select "Default Tenant" from the dropdown Roles: Leave empty (we're using ABAC with attributes, not role assignments)
You can fill the user form with your own details.
Click Continue without a role when the warning appears, this is expected because we’re using attribute-based policies instead of role assignments.
Click Save to create the user.

After creating the user, click the three-dot menu next to their name and select "Edit Attributes". In the JSON editor, add these attributes:
{
"spending_limit": 2000,
"department": "Engineering",
"job_level": "Senior",
"approval_limit": 0
}
Click Save to store the attributes.

Repeat the same process for the other four users. You can find their complete details (email, name, and attributes) in this github gist.
Once all five users are created with their attributes, your user list should look like this:

Your Directory is now ready. The ABAC policies you configured in the Permit.io Dashboard will automatically use these user attributes during your n8n authorization checks.
When creating users, make sure to use an email address you can access as the Key. The n8n workflow uses this email to send and receive messages during expense submission.
If you prefer to create the entire authorization schema in one step, you can use the Permit CLI. If you don’t have it installed yet, install it with:
npm install -g @permitio/cli
This method applies the Expense Approval System template automatically, including user attributes, resource attributes, actions, and ABAC rules.
Step 1 — Log in
Authenticate and select your project + environment:
permit login
Step 2 — View available templates
This shows all environment templates bundled with the CLI:
permit env template list
Step 3 — Apply the Expense Approval template
Run the following command to create the full schema:
permit env template apply expense-approval-system-policy.tf
Once applied, your environment will contain the same configuration described in the Dashboard walkthrough.
With the authorization schema configured in Permit.io and users added to the Directory, you can now build the n8n expense approval workflow that enforces these policies at runtime.
The workflow consists of six nodes:
Log into your n8n instance and create a new workflow.

Once you create a new workflow, you'll see the workflow canvas with options to add your first node.

Before adding the Permit nodes to your workflow, set up the Permit.io credentials in n8n.
Navigate to Credentials:
Select Permit API:
Configure the credential fields:
https://cloudpdp.api.permit.io for Permit.io's cloud PDP
If you're using a local self-hosted PDP container, expose it publicly using a tool like ngrok and use that URL instead (e.g., https://abc123.ngrok.io). Self-hosted PDPs provide better performance and full support for ABAC and ReBAC policies.
Save the credentials:
You'll select these credentials when configuring each Permit node in your workflow.
Add a Webhook node to start your workflow.
Add the Webhook node:
Configure the Webhook node:
The webhook generates a Test URL for development. Copy this URL, you'll use it to send expense submission requests.
Expected payload structure:
The webhook expects a JSON payload with the following structure:
{
"employee_email": "taofeek2sure@gmail.com",
"expense_amount": 1500,
"category": "travel",
"submitter_department": "Engineering",
"urgency": "normal"
}
This payload contains:
Click "Listen for test event" to activate the webhook.

Before moving forward, verify the webhook is receiving data correctly. Temporarily add a "Respond to Webhook" node to complete the workflow and enable testing.
Add Respond to Webhook node:
Configure Respond to Webhook:

Send a test request:
Click "Listen for test event" on the Webhook node, then send a test request using cURL:
curl -X POST https://taofiqworkflows.app.n8n.cloud/webhook-test/f9a8c245-5efb-41ad \
-H "Content-Type: application/json" \
-d '{
"employee_email": "taofeek2sure@gmail.com",
"expense_amount": 1500,
"category": "travel",
"submitter_department": "Engineering",
"urgency": "normal"
}'
Replace
https://taofiqworkflows.app.n8n.cloud/webhook-test/f9a8c245-5efb-41adwith your own webhook Test URL from the Webhook node configuration.
Verify the result:
If successful, you'll see "Node executed successfully" at the bottom right of n8n. Click on the Webhook node's OUTPUT tab or the Respond to Webhook node's INPUT tab to view the received data. You should see your expense payload with all fields under body.

Once verified, you can delete the Respond to Webhook node, we'll add the proper response handling later in the workflow.
Add the Permit node to check if the user can submit the expense.
Add the Permit node:

Configure the Permit node:
Click on the Permit node to open its configuration panel. Rename the node to "Check Expense Permission" for clarity.
{{ $node["Webhook"].json.body.employee_email }}
This expression extracts the employee_email field from the webhook payload. Learn more about n8n expressions.
submitdefaultexpense
The Enable ABAC toggle is important, when enabled, the Permit node automatically extracts all fields from the webhook body (expense_amount, category, submitter_department, urgency) and sends them as resource attributes to Permit.io. The ABAC policies you defined then evaluate these attributes to determine if the user can submit this specific expense.
Send a new webhook request to trigger both nodes:
curl -X POST https://taofiqworkflows.app.n8n.cloud/webhook-test/f9a8c245-5efb-41ad \
-H "Content-Type: application/json" \
-d '{
"employee_email": "taofeek2sure@gmail.com",
"expense_amount": 1500,
"category": "travel",
"submitter_department": "Engineering",
"urgency": "normal"
}'
Replace the webhook URL with your own Test URL.
Check the result:
Click on the Permit node's OUTPUT tab. You should see a response with an allow field:

The response shows:
If allow is true, the user can submit the expense within their spending limit. If false, the expense exceeds their limit or violates other policy conditions.
Add an IF node to route the workflow based on the permission check result.
Add the IF node:
Configure the IF node:
Click on the IF node to open its configuration panel.
{{ $json.allow }}
true
This condition checks the allow field from the Permit node's response. If allow equals true, the workflow follows the true branch. If allow equals false, it follows the false branch.
Understanding the branches:
The IF node creates two output paths:

The workflow now routes expenses based on the authorization decision from Permit.io. Approved expenses proceed to the approval workflow, while rejected expenses receive an immediate response.
When permission is denied, the workflow needs to send a rejection response back to the submitter. Add a Respond to Webhook node to the false branch.
Add Respond to Webhook node:

Configure the node:
Click on the Respond to Webhook node:
This returns the permission decision along with the expense details back to the webhook caller.
This node returns the data from the IF node back to the webhook caller. When permission is denied, the response includes the expense details and the allow: false decision from Permit.io.
Test the rejection flow:
Send a webhook request with an expense amount that exceeds the user's spending limit:
curl -X POST https://taofiqworkflows.app.n8n.cloud/webhook-test/f9a8c245-5efb-41ad \
-H "Content-Type: application/json" \
-d '{
"employee_email": "taofeek2sure@gmail.com",
"expense_amount": 2500,
"category": "travel",
"submitter_department": "Engineering",
"urgency": "normal"
}'
Replace the webhook URL with your own Test URL.
Since Taofeek's spending limit is $2,000, this $2,500 expense should be rejected. The workflow follows the false branch and returns a response indicating the expense was denied.

When permission is granted, the workflow needs to find who can approve this expense. Add another Permit node to the true branch to get authorized approvers.
Add the Permit node:
Get Authorized Approvers
Configure the Permit node:
Click on the Permit node and rename it to "Get Authorized Approvers".
approveexpensedefault{
"expense_amount": "{{ $node['Webhook'].json['body']['expense_amount'] }}",
"category": "{{ $node['Webhook'].json['body']['category'] }}",
"submitter_department": "{{ $node['Webhook'].json['body']['submitter_department'] }}"
}

This node queries your Permit.io environment to find all users who have permission to approve this specific expense based on the ABAC policies. The response includes users from user sets like department_managers, senior_managers, or finance_team, depending on the expense amount, department, and other attributes.
The authorized users list will be used by the next node to send approval notifications.
Test the authorized users lookup:
Click "Execute step" on the Get Authorized Approvers node to see who can approve this expense.
Check the OUTPUT tab. You should see a response showing authorized users:
{
"resource": "expense:*",
"tenant": "default",
"users": {
"abumahfuz21@gmail.com": [
{
"user": "abumahfuz21@gmail.com",
"tenant": "default",
"resource": "resourceset_all_5fexpenses",
"role": "userset_finance_5fteam"
}
]
}
}

The response shows that James Wilson (Finance team member) is authorized to approve this $1,500 Engineering expense. Since finance team members can approve any expense regardless of amount or department, he appears in the authorized users list.
For a $6,000 expense, you'd see senior managers in the results instead, as those expenses require senior manager approval.
Add a Send Email node to notify authorized approvers about the pending expense.
Add the Send Email node:

Configure SMTP credentials:
Before configuring the email, you need to set up SMTP credentials. Click on "Credential to connect with" and select "Create New Credential".
Fill in your SMTP details:
Configure the email:
{{ Object.keys($('Get Authorized Approvers').first().json.users)[0] }}
This expression gets the email address of the first authorized approver from the Get Authorized Approvers node's output.
Expense Approval Required - ${{ $node['Webhook'].json.body.expense_amount }}
<h3>Expense Approval Request</h3>
<p><strong>Employee:</strong> {{ $node['Webhook'].json.body.employee_email }}</p>
<p><strong>Amount:</strong> ${{ $node['Webhook'].json.body.expense_amount }}</p>
<p><strong>Category:</strong> {{ $node['Webhook'].json.body.category }}</p>
<p><strong>Department:</strong> {{ $node['Webhook'].json.body.submitter_department }}</p>
<p><strong>Urgency:</strong> {{ $node['Webhook'].json.body.urgency }}</p>
<br>
<p>Please review and approve this expense.</p>

The workflow is now complete. When an expense is submitted and authorized, the email node sends a notification to the appropriate approver with all the expense details.

Let's test the expense approval system with three scenarios to demonstrate how ABAC policies control workflow routing.
Send an expense submission within the user's spending limit:
curl -X POST https://taofiqworkflows.app.n8n.cloud/webhook-test/f9a8c245-5efb-41ad \
-H "Content-Type: application/json" \
-d '{
"employee_email": "taofeek2sure@gmail.com",
"expense_amount": 1500,
"category": "travel",
"submitter_department": "Engineering",
"urgency": "normal"
}'
Replace the webhook URL with your own Test URL.
Expected Result:
The workflow grants permission since $1,500 is within Taofeek's $2,000 spending limit. The Get Authorized Approvers node identifies James (Finance team) as an authorized approver, and an email is sent with the expense details.


Send an expense that exceeds the user's spending limit:
curl -X POST https://taofiqworkflows.app.n8n.cloud/webhook-test/f9a8c245-5efb-41ad \
-H "Content-Type: application/json" \
-d '{
"employee_email": "taofeek2sure@gmail.com",
"expense_amount": 2500,
"category": "travel",
"submitter_department": "Engineering",
"urgency": "normal"
}'
Expected Result:
The workflow denies permission since $2,500 exceeds Taofeek's $2,000 limit. The workflow follows the false branch and returns a rejection response. No email is sent.


Send an expense from a user in a different department with a lower spending limit:
curl -X POST https://taofiqworkflows.app.n8n.cloud/webhook-test/f9a8c245-5efb-41ad \
-H "Content-Type: application/json" \
-d '{
"employee_email": "emma.davis@company.com",
"expense_amount": 400,
"category": "supplies",
"submitter_department": "Sales",
"urgency": "normal"
}'
Expected Result:
The workflow grants permission since $400 is within Emma's $500 spending limit. The ABAC policies evaluate Emma's department (Sales) and expense attributes to determine the appropriate approver.


These tests demonstrate how Permit.io's ABAC policies dynamically control workflow behavior based on user and resource attributes. The same workflow handles different users, departments, and expense amounts without any code changes—all authorization logic is centralized in Permit.io and evaluated at runtime.
This guide set out to solve an identified gap in n8n workflows: the lack of runtime authorization for during n8n workflow execution. While n8n provides platform-level access control for who can build and edit workflows, it doesn't natively support checking whether users can perform specific actions during workflow execution.
We addressed this by implementing a complete expense approval system using Permit.io's ABAC policies integrated with n8n through the @permitio/n8n-nodes-permitio community node. The implementation involved four key stages:
The result is a centralized authorization system where policy changes in Permit.io instantly apply across all n8n workflows, no workflow modifications or redeployment required. When a department manager's approval limit increases or a user changes departments, the authorization decisions automatically reflect these updates. This eliminates the need for custom database lookups, hardcoded IF nodes, and scattered authorization logic across multiple workflows.
Further Reading:

Full-Stack Software Engineer and Technical Writer with a passion for creating scalable web and mobile applications and translating complex technical concepts into clear, actionable content.