-
Notifications
You must be signed in to change notification settings - Fork 0
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
endExample 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
endController 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
endRequest 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
endYou 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