“Access Plugin” doesn't appear in Permissions list Non-admin users are not able to access my plugin in the CP. With admin rights, everything is fine—the templates are accessible, but all the other users are getting 403.

I've tried to build the plugin based on Craft 3 best practices and used:

EVENT_REGISTER_CP_NAV_ITEMS - to register nav items

EVENT_REGISTER_CP_URL_RULES - to register url rules like this: 

$event-&gt;rules['plugin-name'] =  ['template' =&gt; 'plugin-handle/index'];

What am I missing? 

Should I add / register something else in the plugin's init()? 

The permission checkbox for the plugin doesn't even show up in User(Group) Permissions.

I'm not using controllers because routing to templates are working fine and seems to be enough. Is that a problem?
If your plugin has a CP section (specifically, the main Plugin class declares a $hasCpSection property, and it’s set to true), you should get an “Access My Plugin Name” option near the top of the permissions list—this might be enough to grant access to your plugin's controllers. See how this is enforced, here.

If you want to use more granular permissions, you'll have to check that box, plus anything you register via the UserPermissions::EVENT_REGISTER_PERMISSIONS event. Here's an example of that being done in a stripped-down main plugin class:


&lt;?php

use Craft;

use craft\base\Plugin as BasePlugin;
use craft\events\RegisterUserPermissionsEvent;
use craft\services\UserPermissions;

use yii\base\Event;

class MyPlugin extends BasePlugin {
    // ...

    public function init()
    {
        Event::on(
            UserPermissions::class,
            UserPermissions::EVENT_REGISTER_PERMISSIONS,
            function (RegisterUserPermissionsEvent $event) {
                // Each "group" is just a key in the Permissions array, and you can have 
                $event-&gt;permissions[Craft::t('my-plugin', 'My Plugin Features')] = [
                    // You might have discrete feature permissions, or different ones for `access` vs `modify`:
                    'my-plugin-access-first-feature' =&gt; [
                        'label' =&gt; Craft::t('my-plugin', 'Can Access Feature A')
                        // You can also use the `nested` key here to group permissions!
                    ],
                    'my-plugin-access-second-feature' =&gt; [
                        'label' =&gt; Craft::t('my-plugin', 'Can Access Feature B')
                    ]
                ];
            });
    }

    // ...
}


In a controller action, you can call $this-&gt;requirePermission('my-plugin-access-first-feature'); to lock down access.


  Also worth noting: Some plugins (like Commerce) use a few abstract controller definitions (i.e. BaseController, BaseCpController, BaseAdminController) to lock down entire bundles of functionality. If you're using this paradigm, you won't be able to easily relax permissions—only tighten—enforced by the parent Controller.