Content API RBAC for Strapi — auth-provider agnostic roles and permissions. This plugin gives you a role management UI in the Strapi admin and a pluggable session resolver so any authentication provider can drive your Content API access control.
Caution
This plugin is in BETA state. It is by no means considered stable and should not be used in production. If you want to contribute to its development, please contact any of the maintainers.
- ✅ Role management UI in the Strapi admin panel
- ✅ Fine-grained permissions per content type (find, findOne, create, update, delete)
- ✅ Plugin endpoint permissions support
- ✅ Two default roles created on first run: Public and Authenticated
- ✅ Pluggable session resolver — wire in any auth provider
- ✅ Auth-provider agnostic — works standalone or paired with
plugin-better-auth - ✅ Automatically extends your user content type with a
rolesrelation - ✅ Works with Strapi v5+
npm install @strapi-community/plugin-api-permissions
# or
yarn add @strapi-community/plugin-api-permissions
# or
pnpm add @strapi-community/plugin-api-permissionsAdd the plugin to your Strapi configuration:
// config/plugins.ts
export default {
'api-permissions': {
enabled: true,
},
};The plugin needs to know which content type represents your users so it can count role members and reassign users when a role is deleted. If you are using plugin-better-auth, this is resolved automatically. Otherwise, set the user_uid option:
// config/plugins.ts
export default {
'api-permissions': {
enabled: true,
config: {
user_uid: 'plugin::users-permissions.user',
},
},
};The session resolver is called on every Content API request. It receives the Koa context and must return the current user and their roles, or null for unauthenticated requests. Register it in your Strapi bootstrap:
// src/index.ts
import type { Modules } from '@strapi/strapi';
export default {
async bootstrap({ strapi }) {
strapi
.plugin('api-permissions')
.service('session')
.registerSessionResolver(async (ctx) => {
const token = ctx.request.headers.authorization?.replace('Bearer ', '');
if (!token) return null;
const session = await myAuthProvider.verifyToken(token);
if (!session) return null;
const user = await strapi.documents('plugin::my-auth.user').findFirst({
filters: { id: session.userId },
});
const roles = await strapi.documents('plugin::api-permissions.role').findMany({
filters: { type: session.roleType },
}) as Modules.Documents.Document<'plugin::api-permissions.role'>[];
return { user, roles };
});
},
};When plugin-better-auth is installed alongside this plugin, the session resolver is registered automatically — no manual setup required. Just enable both plugins:
// config/plugins.ts
export default {
'better-auth': {
enabled: true,
},
'api-permissions': {
enabled: true,
},
};Users authenticated through Better Auth are matched against the Authenticated role. Unauthenticated requests fall back to the Public role.
On startup the plugin:
- Extends your user content type with a
rolesmanyToMany relation pointing toplugin::api-permissions.role. - Registers a
content-apiauthentication strategy with Strapi that runs on every Content API request. - Seeds the database with Public and Authenticated roles if none exist yet.
On each Content API request the strategy:
- Calls your registered session resolver with the Koa context.
- Loads the permissions for the resolved role(s), falling back to the Public role for unauthenticated requests.
- Generates a CASL ability from those permissions and attaches it to the request.
Navigate to Settings → API Permissions → Roles to manage roles. From there you can:
- Create custom roles with any combination of content type and plugin permissions.
- Edit which actions (find, findOne, create, update, delete) are enabled per role.
- Delete roles — users are automatically reassigned to the Public role on deletion.
Note
The Public role cannot be deleted.
See the LICENSE file for licensing information.