Unauthorized credential access blocked in dynamic parameter requests
A security gap in n8n's dynamic node parameter endpoints allowed users to request other users' credentials by including credential IDs in API payloads. The vulnerability is now patched.
A missing authorization check in n8n's workflow automation platform allowed one user to access another user's credentials through dynamic node parameter endpoints. When a workflow node requests dynamic options, resource locators, or other parameters that depend on a credential, the system previously validated credential ownership only when a workflow ID was also present. Without a workflow ID in the request, credential checks were skipped entirely — leaving the authorization gate open.
The fix ensures credential IDs supplied in dynamic parameter requests are validated against the requesting user's permissions, regardless of whether a workflow ID accompanies the request. If a user includes a credential ID they don't own in any dynamic node parameter endpoint — /options, /resource-locator-results, /resource-mapper-fields, /local-resource-mapper-fields, or /action-result — the request now fails with a 403 Forbidden response before any parameter resolution occurs.
This closes a pre-authorization vulnerability in the automation platform's CLI package. Integration tests were updated to use the real dependency-injected service instead of mocks, ensuring credential enforcement is verified end-to-end.
View Original GitHub Description
Summary
DynamicNodeParametersService.refineResourceIds now validates that the requesting user has credential:read access to all credential IDs supplied in the request body. This check applies across all dynamic node parameter endpoints (/options, /resource-locator-results, /resource-mapper-fields, /local-resource-mapper-fields, /action-result).
Previously, refineResourceIds only scoped workflow access and would return early when no workflowId was present, leaving credential IDs in the payload unchecked. Inaccessible credential IDs are now rejected with 403 Forbidden before any parameter resolution occurs.
The integration tests for DynamicNodeParametersController have been migrated from a fully-mocked service to the real DI- resolved instance, so that credential enforcement is exercised end-to-end.
Testing
Automated
Unit tests cover DynamicNodeParametersService.refineResourceIds directly:
- Requests with no credentials pass through unchanged
- Requests where the user owns all supplied credential IDs are allowed
- Requests containing any inaccessible credential ID throw
ForbiddenError - Credential check runs regardless of whether
workflowIdis present
Integration tests cover the full HTTP layer across all five endpoints, using a real member user and a credential owned by a different user.
Manual
Prerequisites: Two accounts — an owner and a member. A node that loads dynamic options using a credential (e.g. OpenAI, Notion, Google Sheets).
Set up
- Log in as the owner and create a credential (e.g. an OpenAI API Key). Note the credential ID from the URL or network tab.
- Do not share that credential with the member.
Verify the block (negative case)
- Log in as the member in a separate browser/incognito window.
- Open a workflow and add a node that uses the credential type created above.
- In the credential dropdown, do not select any credential.
- Open browser DevTools → Network tab.
- Trigger a dynamic options load (e.g. open a dropdown that fetches remote options, or select a resource locator field).
The editor sends a POST to
/rest/dynamic-node-parameters/options(or similar endpoint). - Intercept that request and replay it — replacing the
credentialsfield with{ "openAiApi": { "id": "<owner-credential-id>", "name": "Stolen" } }. - Expected result:
403 Forbidden.
Verify the allow (positive case)
- As the member, create their own credential of the same type.
- Select that credential in the node panel and trigger the same options load.
- Expected result: Options load successfully (
200 OK).
Verify no regression (empty credentials)
- As any user, open a node that does not require a credential.
- Trigger any dynamic parameter load (options, resource mapper, etc.).
- Expected result: Request succeeds as before.
Related Linear tickets, Github issues, and Community forum posts
https://linear.app/n8n/issue/IAM-422
Review / Merge checklist
- I have seen this code, I have run this code, and I take responsibility for this code.
- PR title and summary are descriptive. (conventions)
- Docs updated or follow-up ticket created.
- Tests included.
- PR Labeled with
Backport to Beta,Backport to Stable, orBackport to v1(if the PR is an urgent fix that needs to be backported)