All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
-
[Performance] Added
# frozen_string_literal: trueacross all Ruby files to significantly reduce string object allocations and lower memory consumption. -
[Performance] Refactored
RepresentationJSON serialization (as_jsonandto_json) to eliminate intermediate Array and Hash allocations, improving CPU performance and memory footprint. -
[Fix]
Representation#as_jsonnow stripsnilvalues when generating JSON payloads. This fixesHTTP 400 Bad Requesterrors caused by strict API requirements in Keycloak 19+ (such as refusingnullforsubGroupCountand other restricted fields). -
[Chore] Expanded the integration test suite to cover 100% of the REST clients against live Keycloak instances.
-
[Chore] Fixed the integration test suite against Keycloak 25 and 26. The
dummyrealm now enables internationalization and unmanaged user attributes, both required since Keycloak 24 forlocaleand custom attributes to be persisted; authorization permissions are deleted before the policy they depend on, since Keycloak 25 cascades that deletion; and permissions are asserted by name rather than by total count, which Keycloak 25 inflates with a seededDefault Permission. Each integration spec now loads the integration helper itself, instead of relying on another spec file having loaded it first. -
[Fix]
Configuration#inspectno longer rendersclient_secret,passwordor the cached access token, showing[FILTERED]instead. The default#inspectprinted every attribute, so these credentials travelled into console sessions,ppoutput, and whatever local-variable dump an exception reporter attaches to a crash. A credential that is not set still renders asnil, so an inspect stays useful when authentication misbehaves. -
[Fix] The access token cache introduced in 2.0.1 is now thread-safe.
-
[Fix] Identifiers are now percent-encoded before being interpolated into a URL path.
-
[Feature]
UserClient#update_passwordaccepts an optionaltemporary:keyword. -
[Performance]
ClientClient#find_by_client_idnow has Keycloak filter with?clientId=instead of listing every client of the realm and comparing in Ruby -
[Feature]
UserClient#listandGroupClient#listaccept optionalfirst:andmax:keywords. Keycloak answersGET /userswith at most 100 entries whenmaxis absent, sousers.listsilently returned a truncated realm with no way to ask for more;GET /groupsapplies no such cap, but both endpoints now page the same way. Calling eitherlistwith no argument sends no query parameter and is unchanged.GroupClient#listpairs a lonemaxwithfirst=0, because Keycloak 19 only paginates that endpoint when both bounds are present and ignoresmaxon its own; this is a no-op from Keycloak 23 on. Verified against live Keycloak 19.0, 23.0 and 26.7.0. -
[Feature]
config.faraday_adapterselects the Faraday adapter requests run through. -
[Fix]
UserClient#impersonatenow takes part in the401replay introduced in 2.0.2. It calledget_redirect_impersonationoutsideexecute_http, so itsAuthorizationheader was built before the replay cleared the cached token: the second attempt resent the very bearer that had just been rejected and failed with the same401. It was the last call in the gem where the replay was inert. -
[Fix]
CamelJson#camelizeno longer raisesNoMethodErroron an empty word. -
[Fix] Query parameters built from an Array are now rendered as the key repeated once per element, instead of the array's Ruby
inspectform: a list value used to be sent asname=%5B%22a%22%2C%20%22b%22%5D. An empty array now contributes no parameter at all; anilstill renders asname=, unchanged. -
[Breaking]
ClientAuthzResourceClient#updatenow replaces the submittedurisandscopesinstead of merging them with the ones already stored. It used to union both, so updating twice with the same uri accumulated duplicates and no uri or scope could ever be removed. A key absent from the submitted hash still leaves the stored value untouched; presence is now what decides, not truthiness, souris: []clears the uris andowner_managed_access: falseis honoured rather than read as "not submitted". The method also no longer raises a bareRuntimeErrorwhen a scope has no:name, but aKeycloakAdmin-consistentArgumentError, and no longer raisesTypeErrorwhen the stored resource has nourisat all. -
[Fix]
ClientRepresentation.from_hashno longer produces wrongly typed defaults when the payload omits a field. -
[Fix]
RoleRepresentationandSessionRepresentationno longer define spuriousfrom_hashandfrom_hash=instance accessors. A trailing comma after the last symbol of theirattr_accessorlist continued the argument list into the followingdef, which evaluates to a Symbol, soattr_accessoralso received:from_hash. The class methodself.from_hashwas never shadowed, so parsing was unaffected. -
[Fix]
UserClient#update,#add_groupand#remove_groupbuilt their request outsideexecute_http. They were the only methods on this client that raised a bareFaraday::Errorinstead of the documentedKeycloakAdmin::ApiErrorhierarchy, and the only ones that did not replay a request rejected on a stale401. -
[Fix]
UserClient#create!could return a different user than the one it had just created. It looked the new user up withsearch(email), and Keycloak'ssearchparameter matches a substring of the username, email, first name or last name: creatingpioupioux@email.comwhilevieuxpioupioux@email.comalready existed returned the pre-existing account. The new user's id is now read from theLocationheader of the creation response, likeGroupClient#create!already did. Two consequences: a creation not answered with201 Creatednow raisesKeycloakAdmin::UnexpectedResponseErrorinstead of returningnil, and the returned representation is fetched by id. -
[Fix]
UserClient#create!wrote thelocaleinto the veryattributeshash it was given, mutating the caller's object. -
[Fix]
UserClient#execute_actions_email(and#forgot_password) ignoredconfig.faraday_options. It was the only call in the gem running with no timeout, and any configured SSL or proxy setting was silently dropped. The internalKeycloakAdmin::Resource.putshorthand it relied on is removed; its signature could not carry connection options. -
[Chore] Migrated test suites to use WebMock
stub_requestinstead of stubbingKeycloakAdmin::Resourcedirectly, ensuring tests validate the actual network payload.
- [Breaking] HTTP failures now raise a
KeycloakAdmin::ApiErrorsubclass instead of a bareRuntimeError.- Messages are unchanged, so code matching on the text keeps working.
- Code rescuing
RuntimeErrorexplicitly must be updated. - A create call answering something other than
201 Creatednow raisesKeycloakAdmin::UnexpectedResponseError.
- [Feature] A
401answer now drops the cached access token and replays the request once with a freshly fetched one, so a token revoked before its advertised expiry no longer fails every call until it lapses. - [Fix]
GroupClient#memberscalled ActiveSupport'sObject#tryand raisedNoMethodErroroutside a Rails application. - [Fix]
UserClient#execute_actions_email(and#forgot_password) called ActiveSupport'sNumeric#secondsand raisedNoMethodErroroutside a Rails application whenever alifespanwas passed. - [Fix]
ClientAuthzPolicyClient#find_byopened a second?in its URL, which foldedname,type,firstandmaxinto the value ofpermission; Keycloak silently returned an unfiltered list. - [Fix] Query parameters are now percent-encoded everywhere they are built. Search terms, and the
redirect_uriofUserClient#execute_actions_email, containing a space,&or=used to break the URL or forge extra parameters. - [Fix]
OrganizationClient#buildraisedNameErrorinstead of the intendedArgumentErrorwhendomainswas not anArray.
- [Feature] Requests are now logged through
config.logger(method, URL, and response status) via Faraday's:loggermiddleware. - [Feature] The access token is now cached and reused (tracking
expires_in) instead of being fetched before nearly every call. Previously, caching lived on theClientinstance, but a freshClientsubclass is created for almost every call (e.g.KeycloakAdmin.realm(x).usersbuilds a newUserClient), so the cache was rarely hit in practice; it now lives on the sharedConfiguration. - Publish the gem to RubyGems from Github Actions when a
v*tag is pushed, using RubyGems' Trusted Publishing
- [Breaking] Replaced
rest-clientwithFaradayas the underlying HTTP library. This should be transparent for callers of this gem's own API, butconfig.rest_client_optionsis renamed toconfig.faraday_optionsand its shape changes from rest-client's flat hash to Faraday's connection options (e.g.{ timeout: 5 }becomes{ request: { timeout: 5 } },{ verify_ssl: false }becomes{ ssl: { verify: false } }). See theConfigurationsection of the README. - [Fix]
GroupClient#remove_realm_level_role_name!used Ruby 3.1 hash value omission - [Fix] Strip trailing slashes from the configured
server_url. Keycloak 26 rejects non-normalized request paths with400 {"error":"missingNormalization"}, so aserver_urlsuch ashttp://localhost:8080/produced an unusable//realms/...path. Earlier Keycloak versions tolerated it. - [Chore]
required_ruby_versionis now>= 3.1instead of>= 2.3 - [Chore] CI now tests against Ruby 3.1, 3.2, 3.3 and 3.4.
- [Chore] Bump
http-cookiedependency from 1.1.0 to 1.1.6. - [Chore] Upgrade Docker base image from
ruby:3.2.2-slim-bullseyetoruby:3.3.12-slim-trixie. - [Chore] CI now boots Keycloak 26.7.0 (
quay.io/keycloak/keycloak) instead of the abandonedtillawy/keycloak-github-actions:25.0.1
- [Feature] Client scopes - supported operations:
create!,get,delete,list, andsearch. - [Feature] Client scopes protocol mappers - supported operations:
create!,get,delete,list, andsearch.
- [Feature] Support for Organizations (Multi-tenancy):
- Organization Management:
- Supported operations:
create!,update,get,delete,list, andcount. - Supported searching and filtering via
exact,query, andsearchparameters.
- Supported operations:
- Member Management:
- Added ability to list organization members with pagination and filtering (
members). - Added
members_countto retrieve the total number of members. - Added
get_member,add_member(by user ID), anddelete_member. - Added helper to find all organizations associated with a specific user:
associated_with_member.
- Added ability to list organization members with pagination and filtering (
- Invitations:
- Added
invite_user: Invites a new user via email/name. - Added
invite_existing_user: Invites an existing Keycloak user to the organization by ID.
- Added
- Identity Provider (IdP) Linking:
- Added methods to manage IdPs linked to an organization:
identity_providers,get_identity_provider,add_identity_provider, anddelete_identity_provider.
- Added methods to manage IdPs linked to an organization:
- Organization Management:
- [Feature] Added the ability to list credentials for a given user.
- [Fix] Implemented safe parsing for nested JSON elements within
CredentialRepresentation(handling bothcredentialDataandsecretDatafields). Please refer to the official documentation. - [Breaking] Renamed
CredentialRepresentationattributecreated_date$\rightarrow$ createdDateto align with the Keycloak Admin API.
- Add remove_realm_level_role_name! action on a GroupClient (thanks to @mkrawc)
- Dependencies: Update Gemfile.lock to bump byebug → 12.0.0, rspec → 3.13.2, and related dependencies
- Bug: The 'remove' operation of the 'RoleMapperClient' does not take the global rest options into account
- Client Authorization management support (thanks to @tillawy)
- GitHub-actions setup to execute
rspec(thanks to @tillawy)
- Add group endpoints (get, children, delete), support for group attributes (thanks to @mkrawc)
- GroupClient#save method now can update an existing group (thanks to @mkrawc)
- RoleClient#save method now can update an existing role (thanks to @mkrawc)
- Add/List realm-role/s to a group, Allow role-names with spaces, List groups assigned to role (thanks to @LiquidMagical)
- Search for groups with parameters (thanks to @@tlloydthwaites)
- Get client by ID, Find client by Client ID, Update Client (thanks to @gee-forr)
- Revert the modifications on the feature 'Update a User' introduced in
1.0.22. This implementation had breaking changes such as not being able to update several attributes (first_name,email, etc).
- Be more permissive about the version of
rest-client(~> 2.0) (thanks to @type-face)
- Fetch user's all active sessions (thanks to @prsanjay)
- Check whether a user is locked or not (thanks to @prsanjay)
- Logout users from all the active sessions (thanks to @prsanjay)
- List users who are a member of a group (thanks to @tlloydthwaites)
- Create subgroups (thanks to @neckhair)
- Add subgroups to
GroupRepresentation(thanks to @neckhair) - Expose
BaseRoleContainingResource.resource_id(thanks to @neckhair)
- Remove specific realm roles from user (thanks to @tlloydthwaites)
- Get role by name (thanks to @tlloydthwaites)
- List user realm-level role mappings (thanks to @Kazhuu)
- Delete
Client
- Remove
rest-clientwarning when adding a group (thanks to @tlloydthwaites)
- Delete all "realm" roles mapped to a user
- Update
Gemfile.lockto avoid wrong CVE detections. The version of Rails should always be specified by the parent project. This change has no functional impact.
- Add client role on users
- List client roles
- Allow to use multiple
KeycloakAdmin::Clientin the same environment
- When serializing an array to JSON, force the serialization to use
to_jsonfor each element. In several contexts (e.g. Rails),to_jsonis not used.
- Create
Client - Create
Identity Provider(Breaking change:IdentityProviderRepresentation.configurationhas been renamed toIdentityProviderRepresentation.config) - Add
Identity Provider Mapping - Find service account for a
Client
- List all
Identity Providers - Add Group on Users (thanks to @tomuench)
- Remove Group from Users (thanks to @tomuench)
- Add
totpon Users - Add
required_actionson Users
- Extend
searchfunction to use complex queries (thanks to @hobbypunk90)
- Bug:
rest_client_optionsdefault value does not match the documentation (wasnilby default, should be{}) - Update documentation about client setup (based on Keycloak 11)
- Fix:
Replace request method shorthand with .execute for proper RestClient option support(thanks to @RomanHargrave) - When sending action emails, add lifespan as an optional parameter (thanks to @hobbypunk90)
Thanks to @hobbypunk90
- Support for action emails and send forgot passsword mail
Thanks to @RomanHargrave
- Support for working with federated identity provider (broker) links
- Support for Rails 6
Thanks to @cederigo:
- For a given user, get her list of groups
Thanks to @vlad-ro:
- Get list of client role mappings for a group
- Save client role mappings for a user/group
- Save realm-level role mappings for a user/group
Thanks to @vlad-ro:
- List users
- List clients
- List groups, create/save a group
- List roles, save a role
- List realms, save/update/delete a realm
- Get list of client role mappings for a user
- Support passing rest client options for user save and search
- Support using gem without ActiveSupport
Thanks to @vlad-ro:
- Support passing rest client options
- More documentation
- More tests
- Better handling of timeouts
- Get user
- Update users
- Save a locale when creating a new user
- Client to access Custom REST API configurable-token
- Support of impersonation