Integrating with the Permission framework
Permissions
What is the Permissions framework?
The Backstage permissions framework gives you a structured way to control who can do what inside your plugin. Rather than scattering authorization logic across your route handlers, you define permissions declaratively and let a central policy decide whether to allow or deny each action.
There are two kinds of permissions:
Basic permissions apply to actions that don't relate to a specific resource. Creating a todo is a good example: the action either is or isn't allowed, regardless of which todo you're creating. The policy returns a definitive ALLOW or DENY.
Resource permissions apply to actions on a specific resource. Reading a particular todo is a good example: whether you're allowed might depend on whether you created it. In addition to the basic ALLOW or DENY, the policy can return a CONDITIONAL decision. CONDITIONAL decisions are required to be evaluated against a specific resource and will produce a per-resource ALLOW or DENY.
The framework sits between your route handlers and your business logic. Your handler asks "is this allowed?", the framework consults the active policy, and your handler either proceeds or throws a NotAllowedError.
Common integration points
Most plugins integrate at two levels:
The backend plugin is where you define your permissions, register them with the framework, and enforce them inside your route handlers.
A common package (for example, @internal/plugin-todo-common) is where you export the permission definitions so they can be referenced from anywhere: your backend, your frontend, and any policy that an adopter writes.
The split matters because policy authors need to reference your permission objects when writing their own policies. If those definitions live inside your backend package, you're forcing a dependency on backend code where it doesn't belong.
Creating private TODOs
The goal here is to ensure users can only read their own todos. This is a resource permission because the decision depends on a property of the resource itself.
Define the permission
In your common package, define a resource permission for reading todos:
// plugins/todo-common/src/permissions.ts
import { createPermission } from '@backstage/plugin-permission-common';
export const TODO_RESOURCE_TYPE = 'todo-item';
export const todoReadPermission = createPermission({
name: 'todo.read',
attributes: { action: 'read' },
resourceType: TODO_RESOURCE_TYPE,
});
export const todoPermissions = [todoReadPermission];
The resourceType field ties this permission to a specific kind of resource. Exporting the string as a named constant (TODO_RESOURCE_TYPE) means you can import it in your backend rules rather than repeating the raw string, which prevents subtle mismatches.