Skip to content

[Request]: Article - how should permission be checked when developing plugin #3632

Open

Description

@darius-v

Summary

I cannot find docs on how permissions should be checked when developing plugin.

Why is it needed?

Need official documentation on how permissions should be checked when developing plugin for non internal strapi development. I found in internal doc

https://github.com/strapi/strapi/blob/develop/docs/docs/architecture/06-http-request-path.md?utm_source=kapa

Admin routes authorize with policies instead. Most admin routes list admin::isAuthenticatedAdmin. Routes that need an RBAC action also list admin::hasPermissions with that action. Plugins use their own policies, for example plugin::content-manager.hasPermissions. These policies read ctx.state.userAbility.

For me it is unclear why/if the 2 policies are needed. I have tested by only checking hasPermissions without isAuthenticated and it worked. But maybe I am missing something. Logically it looks like - if is not authenticated , he will not have permissions anyway so looks like checking isAuthenticated is redundant.

Suggested solution(s)

But should we use this way? If so - in the doc at least a link to this should be I guess. And still need to clarify.
Like

Most admin routes list admin::isAuthenticatedAdmin.

this is probalby about internal strapi routes? So thats one of reasons - to have less confusion when not developing internal, but instead developing custom plugins.

I am not sure what exactly to suggest, but first suggestion is just make somethign official non internal I guess.

Related issue(s)/PR(s)

No response

Activity

  1. github-actions commented on Oct 31, 2026

    @github-actions
    Contributor

    🤖 I've analyzed your question and here's what I found:

    Thanks for raising this — it's a great question, and you've clearly done your homework by digging into the internal architecture docs. Let me clarify how permissions should be handled when developing a custom Strapi plugin.


    How permissions work in plugin routes

    admin::isAuthenticatedAdmin vs admin::hasPermissions

    Your intuition is correct: if a user is not authenticated, they won't have any permissions anyway, so in practice, admin::hasPermissions alone is sufficient to protect a route. Here's why both policies exist in Strapi's internal routes:

    • admin::isAuthenticatedAdmin simply checks ctx.state.isAuthenticated — it's a lightweight, fast gate that short-circuits the request before any heavier RBAC logic runs. [isAuthenticatedAdmin source]
    • admin::hasPermissions checks ctx.state.userAbility against specific RBAC actions — it's the full permission check. [hasPermissions source]

    For internal Strapi admin routes, using both is a defensive pattern. For custom plugin routes, you have flexibility depending on your needs.


    Recommended approach for custom plugins

    1. Admin routes — authentication only

    If you just need to ensure the user is logged into the admin panel:

    // /src/plugins/my-plugin/server/src/routes/admin/index.js
    module.exports = {
      type: 'admin',
      routes: [
        {
          method: 'GET',
          path: '/articles',
          handler: 'article.find',
          config: {
            policies: ['admin::isAuthenticatedAdmin'],
          },
        },
      ],
    };

    [Route declaration formats]

    2. Admin routes — with RBAC (recommended for most plugins)

    If you want fine-grained role-based access control, register custom permissions in your plugin's bootstrap function and then protect routes using admin::hasPermissions:

    Register permissions server-side:

    // /src/plugins/my-plugin/server/src/bootstrap.ts
    import type { Core } from '@strapi/strapi';
    
    const bootstrap = ({ strapi }: { strapi: Core.Strapi }) => {
      const actions = [
        {
          section: 'plugins',
          displayName: 'Access the overview page',
          uid: 'overview.access',
          pluginName: 'my-plugin',
        },
      ];
    
      strapi.admin.services.permission.actionProvider.registerMany(actions);
    };
    
    export default bootstrap;

    [Admin permissions guide]

    Protect pages on the admin side using <Page.Protect> or useRBAC:

    // /src/plugins/my-plugin/admin/src/pages/HomePage.tsx
    import { Page } from '@strapi/strapi/admin';
    import pluginPermissions from '../permissions';
    
    const HomePage = () => (
      <Page.Protect permissions={pluginPermissions.accessOverview}>
        {/* page content */}
      </Page.Protect>
    );

    [Admin permissions guide]

    3. Custom plugin policies

    You can also write your own policies (e.g., role-based checks) and reference them by their namespaced name:

    // Reference in route config
    config: {
      policies: [{ name: 'plugin::my-plugin.hasRole', options: { role: 'editor' } }],
    }

    [Policies & middlewares]


    Summary

    Goal What to use
    Ensure user is logged in (admin) admin::isAuthenticatedAdmin
    Check specific RBAC permissions Register actions + admin::hasPermissions or <Page.Protect> / useRBAC
    Custom role/logic checks Write a plugin policy (plugin::my-plugin.myPolicy)

    You're right that admin::isAuthenticatedAdmin is somewhat redundant when admin::hasPermissions is present — it's mainly a performance optimization in Strapi's internal routes. For custom plugins, using admin::isAuthenticatedAdmin alone is fine for simple auth gating, and adding RBAC on top is the recommended path for more granular control.


    Regarding the documentation gap you've identified — this is a valid point and worth surfacing. You can submit a documentation improvement request at feedback.strapi.io. This response may also benefit from review by @pwizla to consider adding a clearer explanation of the two-policy pattern to the plugin development docs.

    Hope this clears things up! Feel free to ask follow-up questions. 🙌

    📚 Relevant documentation:


    ℹ️ This response was generated automatically. If this doesn't fully answer your question or if you need further assistance, please mention @pwizla in a comment and a human maintainer will help you.

    You can also try our interactive AI chat for more detailed assistance.

    💡 For feature requests or product feedback, visit feedback.strapi.io.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions