ruby 108 lines · 3 tabs

Rails Policy Authorization with a before_action Concern and 403 Rendering

Shared by codesnips Aug 2026
3 tabs
module Authorizable
  extend ActiveSupport::Concern

  class NotAuthorized < StandardError; end

  included do
    rescue_from NotAuthorized, with: :render_forbidden
  end

  private

  def authorize!(record, action: action_name, user: current_user)
    policy = policy_for(record, user)
    permission = "#{action}?"

    unless policy.respond_to?(permission)
      raise NotAuthorized, "#{policy.class} has no rule for #{permission}"
    end

    raise NotAuthorized unless policy.public_send(permission)
    record
  end

  def policy_for(record, user)
    klass = "#{record.class.name}Policy".constantize
    klass.new(user, record)
  end

  def render_forbidden
    respond_to do |format|
      format.html { render file: Rails.root.join("public/403.html"), status: :forbidden, layout: false }
      format.json { render json: { error: "forbidden" }, status: :forbidden }
    end
  end
end
3 files · ruby Explain with highlit

This snippet shows how a Rails application enforces per-action authorization using a small policy object wired into controllers through a before_action concern that renders a proper 403 Forbidden when access is denied. The pattern keeps controllers thin: each action declares what it needs authorized, and the mechanics of resolving a policy, calling it, and handling failure live in one reusable place.

In Authorizable concern, the authorize! helper is the heart of the design. It looks up a policy class by convention (ArticlePolicy for an Article), instantiates it with current_user and the record, and calls a predicate named after the current action (show?, update?, and so on). When the predicate returns false it raises NotAuthorized, a dedicated error class, rather than rendering inline. A rescue_from NotAuthorized handler centralizes the response so every denial produces the same 403 regardless of which action failed. Resolving the action name from action_name means callers usually just write authorize!(@article) without repeating the permission name.

Raising and rescuing is deliberate: it lets a single before_action short-circuit the request before the action body runs, and it keeps the failure path out of every individual method. The render_forbidden handler responds to both HTML and JSON, so API and browser clients each get an appropriate 403. Returning a machine-readable error code in the JSON branch is friendlier to frontend clients than a bare status.

ArticlePolicy holds the actual rules as plain predicate methods. Because it is an ordinary Ruby object with no framework coupling, the logic — owners can edit and destroy, anyone can read published articles — is trivial to unit test in isolation and to reason about. admin? short-circuits several checks, a common escape hatch.

In ArticlesController, before_action :require_login and a per-action authorize! call combine to guard mutating endpoints. The trade-off of convention-based lookup is that it depends on consistent naming; when a policy or predicate is missing it fails loudly, which is preferable to silently allowing access. This approach suits apps that have outgrown scattered if current_user checks but do not need a full gem.


Related snips

Share this code

Here's the card — post it anywhere.

Rails Policy Authorization with a before_action Concern and 403 Rendering — share card
Link copied