Skip to content

Authorization

Salman Mahmud edited this page Aug 5, 2026 · 2 revisions

RailsIAM provides a database-driven authorization system based on Role-Based Access Control (RBAC) and Permission-Based Access Control (PBAC).

The goal is simple:

"Is this authenticated user allowed to perform this action?"

RailsIAM treats permissions as data, not code.

Roles and permissions are stored in the database, while authorization rules are declared at the controller boundary where requests enter your application.

This keeps:

  • Controllers focused on business logic
  • Models focused on persistence
  • Permissions managed dynamically without code changes and re-deployment

RailsIAM authorization works with:

  • RailsIAM authentication
  • Devise
  • Any authentication system that provides the current authenticated user

Create Roles and Permissions

Before protecting your endpoints, you'll first need to define your roles and permissions.

RailsIAM uses a flexible authorization model where a user's effective permissions can come from multiple sources.

User
 │
 ├── Roles
 │      │
 │      └── Permissions
 │
 ├── Direct User Permissions
 │
 └── Denied User Permissions

Permissions are resolved using the following rule:

(Role Permissions + Direct User Permissions) − User Denied Permissions = Effective Permissions

This allows most permissions to be managed through roles while still supporting exceptions for individual users without creating additional roles.

Example:

# Create roles
admin_role = RailsIam::Role.create!(name: "admin", description: "")
manager_role = "..."

# Create permissions
create_user = RailsIam::Permission.create!(code: "user:create", description: "api/users/create")
update_user = RailsIam::Permission.create!(code: "user:update")
delete_user = RailsIam::Permission.create!(code: "user:delete")


# Assign permissions to roles
admin_role.permissions << [create_user, update_user]
manager_role.permissions << [update_user]

# Create user
admin = RailsIam::User.create!(email: '..', password_digest: '..')
admin1 = RailsIam::User.create!(email: '..', password_digest: '..')
# or Account.create(...) # if you have authentication model

# Assign role
admin.user_roles.create!(role: admin_role)

# Grant additional permission to admin user from admin role
admin.user_permissions.create!(permission: delete_user)

# Revoke a permission to admin1 user from admin role
admin1.user_denied_permissions.create!(permission: update_user)

# show current_user roles
admin.role_names
# => ["admin"]

# show current_user permissions
admin.permission_codes
# => ["user:create", "user:update", "user:delete"]
admin1.permission_codes
# => ["user:create"]



# show all the denied permission for a user
admin1.denied_permissions => [update_user]
# show all the specific permission that is granted to a user
admin.granted_permissions => [delete_user]

# check how permission resolve
RailsIam::Authorization::PermissionResolver.call(admin)
# check how roles resolve
RailsIam::Authorization::RoleResolver.call(admin)

RailsIAM automatically resolves permissions from all configured sources before every authorization check. Denied permission override the permissions even the permission exists in role permission or user granted permission.


Enable Authorization

Enable authorization by adding the :authorization macro to your base controller.

class ApplicationController < ActionController::Base
  rails_iam :authorization
end

This automatically installs a before_action that performs authorization for every request.

Any controller inheriting from ApplicationController is now protected. If a request reaches a controller action without a matching authorization rule (or skip_authorization), RailsIAM raises an AuthorizationError to prevent accidentally exposing an endpoint.

If you prefer to protect only part of your application, keep ApplicationController unchanged and introduce a dedicated BaseController:

class BaseController < ApplicationController
  rails_iam :authorization
end

Then inherit only the controllers that require authorization:

This approach is useful when your application contains a mix of public and protected controllers. You can also keep a controller public using skip_authorization macro or adding the controller name in configuration excluded_controller_patterns

Once rails_iam :authorization is enabled, the following authorization DSL methods become available in your controllers.

Every protected controller must declare its authorization strategy using one of these methods. If no authorization rule is found for a controller action, RailsIAM raises an AuthorizationError to prevent unintentionally exposing an endpoint.

Method Description
authorize Declares authorization rules based on roles, permissions, only, and except.
authorize_resource Protects the controller using convention based authorization by automatically inferring the required permission from the controller and action name.
skip_authorization Excludes the entire controller or selected actions from authorization. Supports the optional only and except options.

Authentication Requirement

Authorization requires an authenticated user. Otherwise it will raise :unauthorized error.

RailsIAM retrieves the current user using config.current_user_method.

config.current_user_method = :current_user

The authentication source can be:

  • RailsIAM JWT authentication
  • Devise
  • Custom authentication system

RailsIAM only needs a method that returns the authenticated user.


Error Handling

By default, Rails IAM raises an authorization error (403 Forbidden) when a user is not permitted to access an endpoint.

RailsIam::Authorization::Exceptions::AuthorizationError

You can override the response message by adding render_rails_iam_authorization_error in your base controller.

  def render_rails_iam_authorization_error(exception)
    render json: {
      message: "your custom message",
      status:  403,
    }, status:  :unauthorized
  end

Devise and Traditional Rails applications

Rails IAM also works seamlessly with traditional Rails applications that render HTML.

When authorization is enabled, Rails IAM first resolves the current authenticated user. If no authenticated user is available (a guest user visiting a protected page), it raises an AuthenticationError before evaluating authorization rules.

For this reason, traditional Rails applications should handle both authentication and authorization errors.

Example:

class ApplicationController < ActionController::Base

  rails_iam :authorization

  def render_rails_iam_authorization_error(exception)
    flash[:alert] = exception.message
    redirect_to root_path
  end

  # this will not override devise exception for unauthenticated user
  def render_rails_iam_authentication_error
    flash[:alert] = exception.message
    redirect_to new_session_path
  end


end

This allows RailsIAM authorization to work naturally with both:

  • API-only Rails applications
  • Traditional Rails applications with HTML views

Clone this wiki locally