Skip to content

Testing Controller and Request Authorization

Salman Mahmud edited this page Aug 5, 2026 · 1 revision

Follow the steps below to write authorization tests for your controllers and request specs. Create a small helper to generate authentication headers for your tests.

# spec/support/authorization_builder.rb

class AuthorizationBuilder
  def initialize(user, test_context)
    @user = user
    @test_context = test_context
  end

  def with_permission(permission_code)
    permission =  FactoryBot.create(:permission, code: permission_code)
    FactoryBot.create(:user_permission, user: @user, permission: permission)
    self
  end


  def headers
    access_token = RailsIam::Authentication::JwtEncoder.call(@user, jti: SecureRandom.uuid)


    request_env = Rails.application.env_config.deep_dup
    rails_request = ActionDispatch::Request.new(request_env)

    cookie_jar = ActionDispatch::Cookies::CookieJar.build(rails_request, {})
    cookie_jar.encrypted[RailsIam.configuration.authentication.access_token_key] = {
      value: access_token,
      expires: 15.minutes.from_now
    }

    cookie_header_value = cookie_jar.to_header
    @test_context.cookies[RailsIam.configuration.authentication.access_token_key] = cookie_jar[RailsIam.configuration.authentication.access_token_key]

    { 'Cookie' => cookie_header_value }
  end
end

Add helper module for controller and request authorization:

# spec/support/controller_authorization_helper.rb
module ControllerAuthorizationHelper
  def authenticate_as(user, permissions: [])
    allow(controller).to receive(:authenticate!).and_return(true)
    allow(controller).to receive(:current_user).and_return(user)

    Array(permissions).each do |permission|
      AuthorizationBuilder.new(user, self).with_permission(permission)
    end

    user
  end
end

# spec/support/request_authorization_helper.rb
module RequestAuthorizationHelper
  def authorization_for(user)
    AuthorizationBuilder.new(user)
  end
end

Include it in your RSpec configuration:

# spec/rails_helper.rb

Dir[Rails.root.join('spec/support/**/*.rb')].sort.each do |f|
  require f
end


RSpec.configure do |config|
  config.include RequestAuthorizationHelper, type: :request
  config.include ControllerAuthorizationHelper, type: :controller
end

Example Controller

Assume the application has the following controller:

class ProductsController < ApplicationController
  rails_iam :authentication, :authorization

  authorize permissions: "product:show", only :show
  authorize permissions: "product:create", only :create

  def show; end

  def create; end
end

Controller Spec Example

RSpec.describe ProductsController, type: :controller do
  let(:user) { create(:user) }

  before do
    create(:role, name: "admin")
  end

  describe 'POST #create' do
    before { authenticate_as(user, permissions: [ 'product:create' ]) }

    let(:valid_params) do
      {
        product: {
          name: "test name",
          description: "test description"
        }
      }
    end

    it 'creates a new user' do
      expect do
        post :create, params: valid_params
      end.to change(Product, :count).by(1)
    end
  end
end

Request Spec Example

require "rails_helper"

RSpec.describe "/products", type: :request do
  let(:product) { create(:product) }
  let(:user) { create(:user) }

  describe 'GET /products/:id' do
    context 'show action' do
      it 'returns 200 OK' do
        session_headers = authorization_for(user).with_permission("product:show").headers
        get "/products/#{product.id}", headers: session_headers
        expect(response).to have_http_status(:ok)
      end
    end
  end
end

You can also check 401, 403 and not 403 when proper permission are given by creating a shared test spec.

# spec/support/shared_examples/authorization_examples.rb

RSpec.shared_examples 'authorized' do |http_method:, path:, code: 'not-a-code'|
  context 'when not authenticated' do
    it 'returns 401 Unauthorized' do
      public_send(http_method, instance_exec(&path), headers: {})
      expect(response).to have_http_status(:unauthorized)
    end
  end

  context 'when authenticated but no permission' do
    it 'returns 403 Forbidden' do
      session_headers = authorization_for(user).headers
      public_send(http_method, instance_exec(&path), headers: session_headers)
      expect(response).to have_http_status(:forbidden)
    end
  end

  context 'when authenticated with proper permission' do
    it 'should not return 403 Forbidden if  proper permission used' do
      session_headers = authorization_for(user).with_permission(code).headers
      public_send(http_method, instance_exec(&path), headers: session_headers)
      expect(response).not_to have_http_status(:forbidden)
    end
  end
end

Then in your request spec

  describe 'GET /products/:id' do
    it_behaves_like 'authorized', http_method: :get, path: -> { "/products/#{product.id}" }, code: "product:show"

    context 'show action' do
      it 'returns 200 OK' do
        # ....
      end
    end
  end

Clone this wiki locally