Rails Keycloak Authorization
You can find more in this blog.
Rails middleware to authorize requests using Keycloak and gem keycloak-admin-ruby.
This gem uses JWT token to authorize requests. To read more how this gem works:
For the moment it only support permission_resource_format=uri. it does not support permission_resource_format=resource.
It does not support rails cookie-based-sessions, so it is only suitable for APIs.
This gem uses regular-expression for URLs matching, so it is very powerful and flexible.
How it works
This gem is a middleware that checks if the request is authorized by Keycloak. It will check if the request's token is valid and if the user has the required roles to access the requested resource.
Keycloak setup for authorization has many options, the following conventions were followed building this gem:
Rails component | Keycloak component |
---|---|
Controller | Authz Resource |
Controller Action | Authz Scope |
Route | permission subject |
Flow
sequenceDiagram
actor User
User->>Application: Request ${URL} with ${JWT_TOKEN}
create participant Keycloak
Application-->>Keycloak: is ${JWT_TOKEN} authorized for ${URL}?
note right of Keycloak: Keycloak will validate the token <br/> and check if the user has the required roles
destroy Keycloak
Keycloak-->>Application: ${JWT_TOKEN} is authorized for ${URL}
destroy Application
Application-->>User: Response ${URL} with ${DATA}
Configuration
In order to use this gem, you need to configure it in an initializer file. You can create a new file in config/initializers/rails_keycloak_authorization.rb
with the following content:
RailsKeycloakAuthorization.keycloak_auth_client_realm_name = ENV.fetch("KEYCLOAK_AUTH_CLIENT_REALM_NAME", "dummy")
RailsKeycloakAuthorization.keycloak_auth_client_id = ENV.fetch("KEYCLOAK_AUTH_CLIENT_ID", "dummy-client")
RailsKeycloakAuthorization.keycloak_server_url = ENV.fetch("KEYCLOAK_SERVER_URL", "http://localhost:8080")
RailsKeycloakAuthorization.keycloak_server_domain = ENV.fetch("KEYCLOAK_ADMIN_SERVER_DOMAIN", "localhost")
RailsKeycloakAuthorization.keycloak_admin_realm_name = ENV.fetch("KEYCLOAK_ADMIN_REALM_NAME", "master")
RailsKeycloakAuthorization.keycloak_admin_client_id = ENV.fetch("KEYCLOAK_ADMIN_CLIENT_ID", "keycloak-admin")
RailsKeycloakAuthorization.keycloak_admin_client_secret = ENV.fetch("KEYCLOAK_ADMIN_CLIENT_SECRET", "keycloak-admin-client-secret-xxx")
RailsKeycloakAuthorization.match_patterns = [
/^\/organizations(\.json)?/,
/^\/api/,
/internal/
]
Add the route to the UI helper config/routes.rb
:
# make sure to change the constraint to suite your security
mount RailsKeycloakAuthorization::Engine, at: "/rka", constraints: lambda { |request| request.remote_ip == "127.0.0.1" }
How to easily test it?
Create development environment with Keycloak and Tofu:
- checkout the source-code of this project
git checkout https://github.com/tillawy/rails_keycloak_authorization.git
cd rails_keycloak_authorization
- Run keycloak in a Docker container
cd docker
docker-compose up
- verify keycloak is running at
http://localhost:8080
, username:admin
, password:admin
- Run tofu to setup keycloak realm & client
brew install opentofu
cd ../tofu
tofu -chdir=tofu init
tofu -chdir=tofu apply -auto-approve
Running the previous steps should:
- Start Keycloak server
- Create realm called:
Dummy
- Create openid-client called:
dummy-client
in realmdummy
with:- client secret
dummy-client-super-secret-xxx
- valid_redirect_uri
http://localhost:3000/*
- client secret
- Create user
[email protected]
with passwordtest
- Create openid-client called:
keycloak-admin
in realmmaster
with:- client secret
keycloak-admin-client-secret-xxx
- role to manager users in realm
dummy
- client secret
Run the server:
bundle exec rails s
make the first request (should fail) Authorization Failed
:
bash test/curl/test.curl.bash
How let us setup Authorization:
- Open rka http://localhost:3000/rka/management/
- On the first tab
Rails Routes
- The first route
/organizations(.:format)
, click inspect - Click on
Create Resource?
to create Authz Resource for controller - Click on
Create Scope?
to create resource for controller action - Click on
Attach scope index to resource
to attach the scope (action: index) to the resource (controller: organizations_controller) - Select the second tab
Keycloak Policies
- From the Role dropdown list select
default-roles-dummy
- Click
Create
- Select the third tab
Keycloak Permissions
- From the Policy dropdown list select
RKA-Policy
- From the Resource dropdown list select
organization_controllers
- Another dropdown will appear Select Scope, select
index
- Click
Create
Now let us run the test bash test/curl/test.curl.bash
again, it should pass.
Installation
Add this line to your application's Gemfile:
gem "rails_keycloak_authorization"
And then execute:
$ bundle
Contributing
Contribution directions go here.
License
The gem is available as open source under the terms of the MIT License.