MCPInstall

n8n:protect-endpoints

Applies n8n's RBAC scope decorators to REST endpoints. Use when creating a new @RestController, adding any @Get/@Post/@Put/@Patch/@Delete route to an existing controller, or reviewing endpoint authorization. Every authenticated endpoint must be gated by @ProjectScope or @GlobalScope.

Installs
242
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/n8n-io/n8n --skill n8n:protect-endpoints
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads n8n:protect-endpoints automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

Protect REST endpoints with RBAC

Rule: every authenticated route on a @RestController MUST carry an access-scope decorator. If you add a route without one, the IDOR/permission bypass is on you.

Decision

URL has :projectId  → @ProjectScope('<resource>:<op>')
URL has no project  → @GlobalScope('<resource>:<op>')
skipAuth: true      → no decorator + comment explaining alternate auth

@ProjectScope succeeds if the user has the scope globally OR in the project named in the URL. @GlobalScope ignores project relations entirely.

Both decorators come from @n8n/decorators. The middleware lives in packages/cli/src/controller.registry.ts (createScopedMiddleware) and resolves access via userHasScopes in packages/cli/src/permissions.ee/check-access.ts.

Apply the decorator

import { Get, Post, ProjectScope, RestController } from '@n8n/decorators';

@RestController('/projects/:projectId/widgets')
export class WidgetsController {
  @Post('/')
  @ProjectScope('widget:create')          // create
  async create(...) { ... }

  @Get('/:widgetId')
  @ProjectScope('widget:read')            // read one
  async get(...) { ... }

  @Get('/')
  @ProjectScope('widget:list')            // list
  async list(...) { ... }

  @Patch('/:widgetId')
  @ProjectScope('widget:update')          // update
  async update(...) { ... }

  @Delete('/:widgetId')
  @ProjectScope('widget:delete')          // delete
  async delete(...) { ... }
}

Conventions:

  • One decorator per route, placed directly under the HTTP-method decorator.
  • Use the most specific scope that fits. Reuse *:update for state-changing actions like publish/unpublish/build unless the resource needs to gate them separately (see workflow:publish for the precedent).
  • Routes without :projectId and not global-only operations are usually a design smell — flag it.

When the scope doesn't exist yet

Add the resource and ops in packages/@n8n/permissions/:

  1. src/constants.ee.ts — add to RESOURCES (alphabetical):
    widget: [...DEFAULT_OPERATIONS, 'execute'] as const,
    
    The Scope union (<resource>:<op> template-literal type) auto-derives.
  2. src/scope-information.ts — add a display name + description per scope.
  3. src/roles/scopes/project-scopes.ee.ts — add to project roles. Match the workflow precedent unless product says otherwise:
    • REGULAR_PROJECT_ADMIN_SCOPES, PERSONAL_PROJECT_OWNER_SCOPES, PROJECT_EDITOR_SCOPES → all CRUDL+execute scopes.
    • PROJECT_VIEWER_SCOPES → read/list/execute only.
    • PROJECT_CHAT_USER_SCOPES → execute only (if applicable).
  4. src/roles/scopes/global-scopes.ee.ts — add to GLOBAL_OWNER_SCOPES (admin inherits via concat()). Do not add to member/chat-user globals — they get scopes via project relations.
  5. Personal-space publishing: if you add a <resource>:publish scope, also append it to PERSONAL_SPACE_PUBLISHING_SETTING.scopes in constants.ee.ts so personal-owner gating matches workflow:publish.
  6. src/roles/custom-role-scopes.ee.ts — add the resource to PROJECT_CUSTOM_ROLE_OPERATIONS with the ops to render in the permissions matrix, in display order. The editor's SCOPES/SCOPE_TYPES and the save-time whitelist PROJECT_CUSTOM_ROLE_SCOPES both derive from it: a resource missing here cannot reach the UI, and a scope missing from it is rejected on save.
  7. Frontend wiring — three files; skipping any of them means the new scopes will not appear in the project-role configuration UI:
    • packages/frontend/@n8n/stores/src/rbac.store.ts — add <resource>: {} to scopesByResourceId (typecheck will fail otherwise).
    • packages/frontend/editor-ui/src/features/roles/project/projectRoleScopes.ts — add the resource to SCOPE_TYPES (the order the resource group appears on the page).
    • packages/frontend/@n8n/i18n/src/locales/en.json — add projectRoles.<resource>:<op> (column label) and projectRoles.<resource>:<op>.tooltip (hover description) for every op, plus projectRoles.type.<resource> (the group header).
  8. Snapshot — update packages/@n8n/permissions/src/__tests__/__snapshots__/scope-information.test.ts.snap to include the new <resource>:* entries.

No DB migration needed — AuthRolesService.init() syncs scopes/roles on every startup. Custom team roles created in the UI are not auto-updated; mention this in the PR description.

Public / unauthenticated routes

{ skipAuth: true } skips the auth middleware → req.user is undefined → adding @ProjectScope would 401 every call. Public routes (third-party webhooks, signed callbacks) must:

  1. Omit the scope decorator.
  2. Authenticate via signature/HMAC verification inside the handler (or another route-specific mechanism).
  3. Carry a comment explaining why no scope is applied, so the next reviewer doesn't try to "fix" it.

Example:

// Third-party webhook callback: do not add @ProjectScope. Auth happens
// via per-platform signature verification inside webhookHandler, and
// :projectId is unused in the (agentId, platform) lookup.
@Post('/:agentId/webhooks/:platform', { skipAuth: true, allowBots: true })
async handleWebhook(...) { ... }

Verify with a route-metadata test

Add a regression test that fails when a future route is added without a scope. Iterate every route on the controller via ControllerRegistryMetadata and assert the gate.

import { ControllerRegistryMetadata } from '@n8n/decorators';
import { Container } from '@n8n/di';
import { WidgetsController } from '../widgets.controller';

const UNAUTHENTICATED_HANDLERS = new Set<string>(); // add public handler names here

const metadata = Container.get(ControllerRegistryMetadata).getControllerMetadata(
  WidgetsController as never,
);
const routeCases = Array.from(metadata.routes.entries()).map(([handlerName, route]) => ({
  handlerName, route,
}));

describe('WidgetsController route access scopes', () => {
  it.each(routeCases)(
    '$handlerName is gated by a project-scoped widget:* check',
    ({ handlerName, route }) => {
      if (UNAUTHENTICATED_HANDLERS.has(handlerName)) {
        expect(route.accessScope).toBeUndefined();
        expect(route.skipAuth).toBe(true);
        return;
      }
      expect(route.accessScope).toBeDefined();
      expect(route.accessScope?.globalOnly).toBe(false);
      expect(route.accessScope?.scope.startsWith('widget:')).toBe(true);
    },
  );
});

Defense in depth (still required)

Decorator alone is not enough when handlers leak data via downstream calls. Service/repository methods should still filter by projectId (or user-scoped helpers like findByUser). The decorator gates who can call this URL; the service gates what they can read. Both, always.

Reference patterns

  • Project-scoped CRUD: packages/cli/src/workflows/workflows.controller.ts, packages/cli/src/credentials/credentials.controller.ts, packages/cli/src/modules/data-table/data-table.controller.ts.
  • Mixed global + project: packages/cli/src/controllers/project.controller.ts.

FAQ

What does the n8n:protect-endpoints skill do?

Applies n8n's RBAC scope decorators to REST endpoints. Use when creating a new @RestController, adding any @Get/@Post/@Put/@Patch/@Delete route to an existing controller, or reviewing endpoint authorization. Every authenticated endpoint must be gated by @ProjectScope or @GlobalScope.

How do I install n8n:protect-endpoints?

Run npx skills add https://github.com/n8n-io/n8n --skill n8n:protect-endpoints in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/n8n:protect-endpoints/ (personal) or .claude/skills/n8n:protect-endpoints/ (project).

Which AI agents can use n8n:protect-endpoints?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is n8n:protect-endpoints safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains n8n:protect-endpoints?

It is published by n8n-io in n8n-io/n8n. It has 242 recorded installs.

More from n8n-io/n8n

View all →

create-pr

n8n-io/n8n

Creates GitHub pull requests with properly formatted titles that pass the check-pr-title CI validation. Use when creating PRs, submitting changes for review, or when the user says /pr or asks to create a pull request.

OfficialCode Quality & Workflow847 installs

content-design

n8n-io/n8n

Product content designer for UI copy. Use when writing, reviewing, or auditing user-facing text: button labels, error messages, tooltips, empty states, modal copy, placeholder text, confirmation dialogs, onboarding flows, or i18n strings. Also use when the user says /copy, /content, or /ux-copy.

OfficialFrontend & Design473 installs

n8n-conventions

n8n-io/n8n

Open the skill to read its SKILL.md instructions.

Official433 installs

n8n:conventions

n8n-io/n8n

Open the skill to read its SKILL.md instructions.

314 installs

reproduce-bug

n8n-io/n8n

Open the skill to read its SKILL.md instructions.

OfficialCode Quality & Workflow306 installs

linear-issue

n8n-io/n8n

Fetch and analyze Linear issue with all related context. Use when starting work on a Linear ticket, analyzing issues, or gathering context about a Linear issue.

OfficialProductivity & Automation305 installs

Popular backend & apis skills

View category →

find-skills

vercel-labs/skills

Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.

OfficialBackend & APIs4.1M installs

entra-app-registration

microsoft/azure-skills

Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.

OfficialBackend & APIs566.7K installs

This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).

Backend & APIs514.8K installs

embedded-captions

heygen-com/hyperframes

Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.

Backend & APIs453.3K installs

entra-agent-id

microsoft/azure-skills

Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).

OfficialBackend & APIs336.6K installs

neon

neondatabase/agent-skills

Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.

Backend & APIs237.1K installs