diff --git a/README.md b/README.md
index 92adec3..db46b7a 100644
--- a/README.md
+++ b/README.md
@@ -43,6 +43,12 @@ Plugins available:
 
 - **NoAuth**: no authentication.
 
+### Authorization
+
+Plugins available:
+
+- **Authorization**: base class to provide an authorization per action, the host application should inherit from it and override the class method `allowed?`.
+
 ### Repository
 
 Plugin available:
@@ -134,6 +140,10 @@ authentication:
   password: 'f1891cea80fc05e433c943254c6bdabc159577a02a7395dfebbfbc4f7661d4af56f2d372131a45936de40160007368a56ef216a30cb202c66d3145fd24380906'
 ```
 
+`authorization_class` (String): a plugin class to use;
+
+> 📚 [Wiki Authentication page](https://github.com/blocknotes/tiny_admin/wiki/Authorization) available
+
 `sections` (Array of hashes): define the admin sections, properties:
 
 - `slug` (String): section reference identifier;
diff --git a/lib/tiny_admin/plugins/authorization.rb b/lib/tiny_admin/plugins/authorization.rb
new file mode 100644
index 0000000..e0546c7
--- /dev/null
+++ b/lib/tiny_admin/plugins/authorization.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module TinyAdmin
+  module Plugins
+    class Authorization
+      class << self
+        def allowed?(_user, _action, _param = nil)
+          true
+        end
+      end
+    end
+  end
+end
diff --git a/lib/tiny_admin/router.rb b/lib/tiny_admin/router.rb
index 73d2e87..d7b5061 100644
--- a/lib/tiny_admin/router.rb
+++ b/lib/tiny_admin/router.rb
@@ -54,19 +54,27 @@ def render_page(page)
     end
 
     def root_route(req)
-      if TinyAdmin.settings.root[:redirect]
-        req.redirect route_for(TinyAdmin.settings.root[:redirect])
+      if authorization.allowed?(current_user, :root)
+        if TinyAdmin.settings.root[:redirect]
+          req.redirect route_for(TinyAdmin.settings.root[:redirect])
+        else
+          page_class = to_class(TinyAdmin.settings.root[:page])
+          attributes = TinyAdmin.settings.root.slice(:content, :title, :widgets)
+          render_page prepare_page(page_class, attributes: attributes, params: request.params)
+        end
       else
-        page_class = to_class(TinyAdmin.settings.root[:page])
-        attributes = TinyAdmin.settings.root.slice(:content, :title, :widgets)
-        render_page prepare_page(page_class, attributes: attributes, params: request.params)
+        render_page prepare_page(TinyAdmin.settings.page_not_allowed)
       end
     end
 
     def setup_page_route(req, slug, page_data)
       req.get slug do
-        attributes = page_data.slice(:content, :title, :widgets)
-        render_page prepare_page(page_data[:class], slug: slug, attributes: attributes, params: request.params)
+        if authorization.allowed?(current_user, :page, slug)
+          attributes = page_data.slice(:content, :title, :widgets)
+          render_page prepare_page(page_data[:class], slug: slug, attributes: attributes, params: request.params)
+        else
+          render_page prepare_page(TinyAdmin.settings.page_not_allowed)
+        end
       end
     end
 
@@ -93,15 +101,19 @@ def setup_collection_routes(req, slug, options:)
       # Index
       if options[:only].include?(:index) || options[:only].include?('index')
         req.is do
-          context = Context.new(
-            actions: custom_actions,
-            repository: repository,
-            request: request,
-            router: req,
-            slug: slug
-          )
-          index_action = TinyAdmin::Actions::Index.new
-          render_page index_action.call(app: self, context: context, options: action_options)
+          if authorization.allowed?(current_user, :resource_index, slug)
+            context = Context.new(
+              actions: custom_actions,
+              repository: repository,
+              request: request,
+              router: req,
+              slug: slug
+            )
+            index_action = TinyAdmin::Actions::Index.new
+            render_page index_action.call(app: self, context: context, options: action_options)
+          else
+            render_page prepare_page(TinyAdmin.settings.page_not_allowed)
+          end
         end
       end
     end
@@ -124,16 +136,20 @@ def setup_member_routes(req, slug, options:)
         # Show
         if options[:only].include?(:show) || options[:only].include?('show')
           req.is do
-            context = Context.new(
-              actions: custom_actions,
-              reference: reference,
-              repository: repository,
-              request: request,
-              router: req,
-              slug: slug
-            )
-            show_action = TinyAdmin::Actions::Show.new
-            render_page show_action.call(app: self, context: context, options: action_options)
+            if authorization.allowed?(current_user, :resource_show, slug)
+              context = Context.new(
+                actions: custom_actions,
+                reference: reference,
+                repository: repository,
+                request: request,
+                router: req,
+                slug: slug
+              )
+              show_action = TinyAdmin::Actions::Show.new
+              render_page show_action.call(app: self, context: context, options: action_options)
+            else
+              render_page prepare_page(TinyAdmin.settings.page_not_allowed)
+            end
           end
         end
       end
@@ -145,20 +161,28 @@ def setup_custom_actions(req, custom_actions = nil, options:, repository:, slug:
         action_class = to_class(action)
 
         req.get action_slug.to_s do
-          context = Context.new(
-            actions: {},
-            reference: reference,
-            repository: repository,
-            request: request,
-            router: req,
-            slug: slug
-          )
-          custom_action = action_class.new
-          render_page custom_action.call(app: self, context: context, options: options)
+          if authorization.allowed?(current_user, :custom_action, action_slug.to_s)
+            context = Context.new(
+              actions: {},
+              reference: reference,
+              repository: repository,
+              request: request,
+              router: req,
+              slug: slug
+            )
+            custom_action = action_class.new
+            render_page custom_action.call(app: self, context: context, options: options)
+          else
+            render_page prepare_page(TinyAdmin.settings.page_not_allowed)
+          end
         end
 
         result[action_slug.to_s] = action_class
       end
     end
+
+    def authorization
+      TinyAdmin.settings.authorization_class
+    end
   end
 end
diff --git a/lib/tiny_admin/settings.rb b/lib/tiny_admin/settings.rb
index 61d69c6..882fe9c 100644
--- a/lib/tiny_admin/settings.rb
+++ b/lib/tiny_admin/settings.rb
@@ -7,6 +7,7 @@ class Settings
     DEFAULTS = {
       %i[authentication plugin] => Plugins::NoAuth,
       %i[authentication login] => Views::Pages::SimpleAuthLogin,
+      %i[authorization_class] => Plugins::Authorization,
       %i[components field_value] => Views::Components::FieldValue,
       %i[components flash] => Views::Components::Flash,
       %i[components head] => Views::Components::Head,
@@ -14,6 +15,7 @@ class Settings
       %i[components pagination] => Views::Components::Pagination,
       %i[content_page] => Views::Pages::Content,
       %i[helper_class] => Support,
+      %i[page_not_allowed] => Views::Pages::PageNotAllowed,
       %i[page_not_found] => Views::Pages::PageNotFound,
       %i[record_not_found] => Views::Pages::RecordNotFound,
       %i[repository] => Plugins::ActiveRecordRepository,
@@ -25,10 +27,12 @@ class Settings
 
     OPTIONS = %i[
       authentication
+      authorization_class
       components
       content_page
       extra_styles
       helper_class
+      page_not_allowed
       page_not_found
       record_not_found
       repository
diff --git a/lib/tiny_admin/views/pages/page_not_allowed.rb b/lib/tiny_admin/views/pages/page_not_allowed.rb
new file mode 100644
index 0000000..329aa5a
--- /dev/null
+++ b/lib/tiny_admin/views/pages/page_not_allowed.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module TinyAdmin
+  module Views
+    module Pages
+      class PageNotAllowed < DefaultLayout
+        def template
+          super do
+            div(class: 'page_not_allowed') {
+              h1(class: 'title') { title }
+            }
+          end
+        end
+
+        def title
+          'Page not allowed'
+        end
+      end
+    end
+  end
+end
diff --git a/sig/tiny_admin/plugins/authorization.rbs b/sig/tiny_admin/plugins/authorization.rbs
new file mode 100644
index 0000000..61005e2
--- /dev/null
+++ b/sig/tiny_admin/plugins/authorization.rbs
@@ -0,0 +1,7 @@
+module TinyAdmin
+  module Plugins
+    class Authorization
+      def self.allowed?: (untyped, untyped, ?untyped?) -> bool
+    end
+  end
+end
diff --git a/sig/tiny_admin/router.rbs b/sig/tiny_admin/router.rbs
index 74f2092..dc92055 100644
--- a/sig/tiny_admin/router.rbs
+++ b/sig/tiny_admin/router.rbs
@@ -4,6 +4,8 @@ module TinyAdmin
 
     private
 
+    def authorization: () -> untyped
+
     def render_page: (untyped) -> void
 
     def root_route: (untyped) -> void
diff --git a/spec/features/plugins/authorization_spec.rb b/spec/features/plugins/authorization_spec.rb
new file mode 100644
index 0000000..374d398
--- /dev/null
+++ b/spec/features/plugins/authorization_spec.rb
@@ -0,0 +1,120 @@
+# frozen_string_literal: true
+
+require 'dummy_rails_app'
+require 'rails_helper'
+
+RSpec.describe 'Authorization plugin', type: :feature do
+  let(:root_content) { "Latest authors\nLatest posts" }
+
+  around do |example|
+    prev_value = TinyAdmin.settings.authorization_class
+    TinyAdmin.settings.authorization_class = some_class
+    example.run
+    TinyAdmin.settings.authorization_class = prev_value
+  end
+
+  before do
+    visit '/admin'
+    log_in
+  end
+
+  context 'with an Authorization class that restrict the root page' do
+    let(:some_class) do
+      Class.new(TinyAdmin::Plugins::Authorization) do
+        class << self
+          def allowed?(_user, action, _param = nil)
+            return false if action == :root
+
+            true
+          end
+        end
+      end
+    end
+
+    it 'disallows the access to the root page when opened', :aggregate_failures do
+      expect(page).to have_content 'Page not allowed'
+      expect { click_on 'Sample page' }
+        .to change { page.find('.main-content').text }.to("Sample page\nThis is just a sample page")
+    end
+  end
+
+  context 'with an Authorization class that restrict a specific page' do
+    let(:some_class) do
+      Class.new(TinyAdmin::Plugins::Authorization) do
+        class << self
+          def allowed?(_user, action, param = nil)
+            return false if action == :page && param == 'sample'
+
+            true
+          end
+        end
+      end
+    end
+
+    it 'disallows the access to the page when opened' do
+      expect { click_on 'Sample page' }
+        .to change { page.find('.main-content').text }.from(root_content).to('Page not allowed')
+    end
+  end
+
+  context 'with an Authorization class that restrict resource index' do
+    let(:some_class) do
+      Class.new(TinyAdmin::Plugins::Authorization) do
+        class << self
+          def allowed?(_user, action, _param = nil)
+            return false if action == :resource_index
+
+            true
+          end
+        end
+      end
+    end
+
+    it 'disallows the access to the page when opened' do
+      expect { click_on 'Posts' }
+        .to change { page.find('.main-content').text }.from(root_content).to('Page not allowed')
+    end
+  end
+
+  context 'with an Authorization class that restrict resource show' do
+    let(:some_class) do
+      Class.new(TinyAdmin::Plugins::Authorization) do
+        class << self
+          def allowed?(_user, action, _param = nil)
+            return false if action == :resource_show
+
+            true
+          end
+        end
+      end
+    end
+
+    before { setup_data(posts_count: 1) }
+
+    it 'disallows the access to the page when opened' do
+      click_on 'Posts'
+      expect { click_on 'Show' }
+        .to change { page.find('.main-content').text }.to('Page not allowed')
+    end
+  end
+
+  context 'with an Authorization class that restrict a specific custom action' do
+    let(:some_class) do
+      Class.new(TinyAdmin::Plugins::Authorization) do
+        class << self
+          def allowed?(_user, action, param = nil)
+            return false if action == :custom_action && param == 'sample_col'
+
+            true
+          end
+        end
+      end
+    end
+
+    it 'disallows the access to the page when opened' do
+      click_on 'Authors'
+      expect { click_on 'sample_col' }
+        .to change { page.find('.main-content').text }.to('Page not allowed')
+    end
+  end
+end
