Authentication (OIDC / OAuth2) and the Admin REST API for Keycloak behind one consistent facade, with hardened JWT validation.
Part of a nine-language polyglot SDK (Java · Python · Node · Go · C# · PHP · Rust · Ruby · Kotlin) — one API surface, isomorphic across all of them: github.com/xzawed/KeyCloakSDK.
0.1.0is on RubyGems — the first stable release. A baregem install keycloak-sdknow resolves it. The earlier0.1.0.rc1is still there but, as before, RubyGems never picks a pre-release unless you ask with--preor name it exactly.
- Ruby 3.2+
- Sync-only (every wrapped gem is synchronous); exception-based error handling under
KeycloakSdk::Error
gem install keycloak-sdk -v 0.1.0Or in a Gemfile:
gem "keycloak-sdk", "0.1.0"A bare gem install keycloak-sdk also works now that a stable release exists; the pin above just keeps the resolved version visible.
Name mismatch, on purpose: the gem is
keycloak-sdk(hyphen) but the require path and module arekeycloak_sdk/KeycloakSdk(underscore) — this avoids colliding with the existingkeycloakgem'sKeycloakmodule.
require "keycloak_sdk"
config = KeycloakSdk::Config.new(
server_url: "https://kc.example.com",
realm: "myrealm",
client_id: "admin-cli",
client_secret: "changeme" # load the real value from an env var / secrets manager
)
client = KeycloakSdk::KeycloakClient.new(config)
# 1) Issue a token via the client-credentials grant. TokenSet#inspect masks every token value.
token = client.auth.client_credentials_token
# 2) Validate it — algorithm pinning, exact iss, aud containment, mandatory exp, nbf, clock skew.
validated = client.auth.validate(token.access_token)
puts "subject=#{validated.subject} aud=#{validated.audience}"
# 3) Admin API — admin is created lazily on first access. create() returns the new user id.
user_id = client.admin.users.create({ username: "alice", enabled: true })
client.admin.users.delete(user_id)
client.closeAudience: validation requires the token's
audto containclient_id. A stock realm does not put the client id in a client-credentials token'saud, so on a default realm either passexpected_audience: "my-api"(the audience your realm actually issues), or add an Audience protocol mapper to the client in Keycloak.
The five admin resources — users / clients / roles / groups / realms — offer symmetric CRUD, and client.admin.raw is the escape hatch to the underlying bearer-authenticated Faraday::Connection.
The SDK replaces the unsafe library defaults rather than inheriting them:
- Algorithm pinning — the header-supplied
algis never trusted, soalg: noneand HS/RS confusion are rejected structurally: the pin is applied before key lookup and signature verification, not after. - Strict claim checks — exact
issmatch,audcontainment, mandatoryexp,nbf, and a bounded clock skew. - DoS-safe JWKS — a refetch is triggered only by an unresolved key ID and never by a bad signature, and is rate-limited to a minimum interval (
jwks_min_refetch, 30s by default). The gate applies on a cold cache too, so it cannot be sidestepped by hitting the SDK before its first successful fetch — no volume of forged tokens makes the SDK issue more than one JWKS request per interval. - OIDC nonce /
id_tokenreplay protection —create_authorization_requestalways issues a nonce (same default asstate:) and puts it on the authorization URL. Pass it back asexchange_code(expected_nonce:)and the SDK fully validates theid_tokenbefore comparing the nonce claim. Omitexpected_nonce:and id_token validation is skipped (same opt-out as the other eight languages). - Secret handling —
Config,TokenSet, andAuthorizationRequestmask secrets and tokens in#inspect(***, no prefix leak), TLS verification is on by default, timeouts are always applied, and redirect-following middleware is never installed (SSRF hardening).
Masking covers this SDK's own #inspect; it cannot cover what your logging framework or a backtrace does with a value you hand it. Ruby has no erasable string type, so the client secret lives in an ordinary String for its lifetime — masking is defence in depth, not an erasure guarantee.
This SDK is pre-1.0. Under SemVer a 0.x minor bump may carry breaking changes, so read the release notes before upgrading. Only the newest released version of each language SDK receives security fixes — there are no LTS lines, and older 0.x releases are not backported to. Full policy: SECURITY.md.
- Project overview — all nine languages, what is identical and what is not
- Changelog — read this before upgrading; breaking changes are listed per language
- Getting started — install and quickstart for this language
- Compatibility — which Keycloak server range and base libraries each published version shipped against
- Deploying a Keycloak server — the server this SDK talks to
- Security policy
- Full example:
examples/quickstart.rb