No release in over 3 years
Maps Keycloak JWT roles to a Pundit-friendly UserContext with helpers and a Rails generator.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

~> 2.3
>= 2.2, < 4.0
>= 0.2.0, < 1.0.0
 Project Readme

verikloak-pundit

CI Gem Version Ruby Version Downloads

Pundit integration for the Verikloak family. This gem maps Keycloak roles from JWT claims (e.g., realm_access.roles, resource_access[client].roles) into a convenient UserContext that Pundit policies can consume.

  • Requires verikloak at runtime and pairs well with verikloak-rails for Rails integrations.
  • Provides a pundit_user hook so policies can use user.has_role?(:admin) etc.
  • Keeps role mapping configurable (project-specific mappings differ).

Features

  • UserContext: lightweight wrapper around JWT claims
  • Delegations: has_role?, in_group?, resource_role?(client, role) helpers for controllers and policies
  • RoleMapper: optional map from Keycloak roles → domain permissions
  • Controller integration: pundit_user provider for Rails controllers
  • Generator: rails g verikloak:pundit:install creates initializer + policy template (with has_permission? support for realm roles plus the configured resource scope)

Installation

bundle add verikloak-pundit

If you're on Rails:

rails g verikloak:pundit:install

This generates:

  • config/initializers/verikloak_pundit.rb
  • app/policies/application_policy.rb (template if missing; optional)

For error-handling guidance, see ERRORS.md.

Quick Start (Rails)

# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  include Pundit
  include Verikloak::Pundit::Controller  # provides pundit_user

  # If you're also using verikloak-rails:
  # before_action :authenticate_user!
end

Your policy can then do:

class NotePolicy < ApplicationPolicy
  def update?
    user.has_role?(:admin) || user.resource_role?(:'rails-api', :editor)
  end
end

Where user is the UserContext provided by pundit_user.

Configuration

# config/initializers/verikloak_pundit.rb
Verikloak::Pundit.configure do |c|
  c.resource_client = "rails-api"   # default client for resource roles
  c.role_map = {                    # optional role → permission mapping
    admin:  :manage_all,
    editor: :write_notes,
    reader: :read_notes
  }
  # Where to find claims in Rack env (when using verikloak/verikloak-rails)
  c.env_claims_key = "verikloak.user"

  # How to traverse JWT for roles
  c.realm_roles_path    = %w[realm_access roles]                      # => claims["realm_access"]["roles"]
  # Lambdas in the path may accept (cfg) or (cfg, client)
  # where `client` is the argument passed to `user.resource_roles(client)`
  c.resource_roles_path = ["resource_access", ->(cfg){ cfg.resource_client }, "roles"]

  # Permission mapping scope for `user.has_permission?`:
  #   :default_resource => realm roles + default client roles (recommended)
  #   :all_resources    => realm roles + roles from all clients in resource_access
  #                         (enabling this broadens permissions to every resource client;
  #                          review the upstream role assignments before turning it on)
  c.permission_role_scope = :default_resource
  # Optional whitelist of resource clients when `permission_role_scope = :all_resources`.
  # Leaving this as nil keeps the legacy "all clients" behavior, while providing
  # an explicit list (e.g., %w[rails-api verikloak-bff]) limits which clients can
  # contribute roles to permission checks.
  c.permission_resource_clients = nil

  # Expose `verikloak_claims` to views via helper_method (Rails only)
  c.expose_helper_method = true
end

Working with other Verikloak gems

  • verikloak-bff: When your Rails application sits behind the BFF, the access token presented to verikloak-pundit typically originates from the BFF (e.g. via the x-verikloak-user header). Make sure your Rack stack stores the decoded claims under the same env_claims_key configured above (the default "verikloak.user" works out of the box with verikloak-bff >= 0.3). If the BFF issues tokens for multiple downstream services, set permission_resource_clients to the limited list of clients whose roles should affect Rails-side authorization to avoid accidentally inheriting permissions meant for other services.
  • verikloak-audience: Audience services often mint resource roles with a service-specific prefix (for example, audience-service:editor). Align your role_map keys with that naming convention so user.has_permission? resolves correctly. If Audience adds its own client entry inside resource_access, add that client id to permission_resource_clients when you need to consume those roles from Rails.

Non-Rails / custom usage

claims = { "sub" => "123", "email" => "a@b", "realm_access" => {"roles" => ["admin"]} }
ctx = Verikloak::Pundit::UserContext.new(claims, resource_client: "rails-api")

ctx.has_role?(:admin)             # => true
ctx.resource_role?(:"rails-api", :writer) # depends on resource_access
ctx.has_permission?(:manage_all)  # from role_map, realm or resource roles

Testing

All pull requests and pushes are automatically tested with RSpec and RuboCop via GitHub Actions. See the CI badge at the top for current build status.

To run the test suite locally:

docker compose run --rm dev rspec
docker compose run --rm dev rubocop -a

When writing specs, call Verikloak::Pundit.reset! in your test teardown to ensure configuration changes do not leak between examples:

RSpec.configure do |config|
  config.after { Verikloak::Pundit.reset! }
end

An additional integration check exercises the gem together with the latest verikloak and verikloak-rails releases. This runs in CI automatically, and you can execute it locally with:

docker compose run --rm -e BUNDLE_FROZEN=0 dev bash -lc '
  cd integration && \
  apk add --no-cache --virtual .integration-build-deps \
    build-base \
    linux-headers \
    openssl-dev \
    yaml-dev && \
  bundle config set --local path vendor/bundle && \
  bundle install --jobs 4 --retry 3 && \
  bundle exec ruby check.rb && \
  apk del .integration-build-deps
'

Contributing

Bug reports and pull requests are welcome! Please see CONTRIBUTING.md for details.

Security

If you find a security vulnerability, please follow the instructions in SECURITY.md.

Operational guidance

  • Enabling permission_role_scope = :all_resources pulls roles from every Keycloak client in resource_access. Review the granted roles carefully to ensure you are not expanding permissions beyond what the application expects.
  • Combine permission_role_scope = :all_resources with permission_resource_clients to explicitly opt-in the clients that may contribute permissions. Leaving the whitelist blank (the default) reverts to the legacy behavior of trusting every client in the token.
  • Leaving expose_helper_method = true exposes verikloak_claims to the Rails view layer. If the claims include personal or sensitive data, consider switching it to false and pass only the minimum required information through controller-provided helpers.

License

This project is licensed under the MIT License.

Publishing (for maintainers)

Gem release instructions are documented separately in MAINTAINERS.md.

Changelog

See CHANGELOG.md for release history.

References