-
Notifications
You must be signed in to change notification settings - Fork 0
Authorization
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
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 by adding the :authorization macro to your base controller.
class ApplicationController < ActionController::Base
rails_iam :authorization
endThis 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
endThen 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. |
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_userThe authentication source can be:
- RailsIAM JWT authentication
- Devise
- Custom authentication system
RailsIAM only needs a method that returns the authenticated user.
By default, Rails IAM raises an authorization error (403 Forbidden) when a user is not permitted to access an endpoint.
RailsIam::Authorization::Exceptions::AuthorizationErrorYou 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
endRails 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
endThis allows RailsIAM authorization to work naturally with both:
- API-only Rails applications
- Traditional Rails applications with HTML views