mcpField
U
using-backstage-permissions

using-backstage-permissions

github.com/vinzscamSecurity445 installsUpdated 9w ago
Repository

About using-backstage-permissions

A Backstage development skill for implementing permission checks in plugins. It explains shared permission declarations and frontend or backend authorization, including resource-specific decisions.

Key features

  1. 01Declare shared plugin permissions
  2. 02Gate frontend UI with permission checks
  3. 03Authorize backend actions
  4. 04Make conditional resource-level decisions
  5. 05Handle asynchronous permission states

Use cases

  1. 01Hiding controls from unauthorized users
  2. 02Checking permission before a route action
  3. 03Restricting updates based on resource ownership

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/vinzscam/backstage-skills --skill using-backstage-permissions

After installing, type/using-backstage-permissionsin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
backend.md
declaring.md
frontend.md
resource-permissions.md

Details

From the project README

Backstage Permissions Patterns

When to use this skill

  • Plugin needs to hide a button, menu, or page from users without the right permission (frontend)
  • Plugin's route handler needs to authorize the caller before performing an action (backend)
  • Defining new permissions for a plugin
  • Checking permissions per-resource ("can update this component", not "can update components in general")

Choose the right pattern

Declaring permissions
Building...PatternReference
A new permission for your plugincreatePermission({ name, attributes }) in a *-common libraryreferences/declaring.md
Frontend (in a plugin component)
SituationPatternReference
Conditionally render UI based on a permissionusePermission({ permission }) hookreferences/frontend.md
Wrap an entire subtree in a permission gate<RequirePermission permission={...}>...</RequirePermission>references/frontend.md
Backend (in a route handler or scheduled task)
SituationPatternReference
Authorize a basic action permissioncoreServices.permissions.authorize([{ permission }], { credentials })references/backend.md
Authorize per-resource (with conditional decisions)permissions.authorizeConditional([{ permission, resourceRef }], { credentials }) + permissionsRegistry.addResourceType(...)references/resource-permissions.md

Two invocation modes

  • Standalone (mid-development): user is editing an existing plugin. Pick the right reference based on what's being added — defining a permission, gating UI, or authorizing on the backend.
  • Post-scaffold (called by creating-backstage-plugin): when intent describes "only admins can…", "the current user must have permission to…", or any access-control language. Add the relevant patterns to the freshly scaffolded plugin.

Common gotchas

  • Permissions are declared once, used twice. The permission object lives in a *-common (or *-permissions-common) library so both the frontend (usePermission) and the backend (permissions.authorize) import the same definition. Don't redeclare the same permission in two places.
  • The host app must enable the permission framework. Adopters set permission.enabled: true in app-config.yaml and install a PermissionPolicy. If permissions aren't enabled, authorize always returns AuthorizeResult.ALLOW — your plugin code is correct, just unenforced. Document the prerequisite in your plugin README.
  • permissions.authorize returns an array matching the order of requests. For a single permission, destructure const [decision] = await permissions.authorize(...) — and the decision is decision.result === AuthorizeResult.ALLOW, NOT a boolean.
  • For resource permissions, prefer authorizeConditional over authorize when the decision can depend on the resource (e.g. "owner can update"). It returns conditional decisions you translate into a database-level filter, avoiding the need to load every candidate resource into memory.
  • usePermission is async. Returns { allowed, loading, error } — render a <Skeleton /> while loading, otherwise users see UI flicker as access is determined.
  • For backend plugins, coreServices.permissions and coreServices.permissionsRegistry come from @backstage/backend-plugin-api. They're plugin-scoped — your plugin gets a PermissionsService that already knows its own pluginId.

Comments

Markdown supported