Skip to main content

Keycloak SDK for Python

Authentication (OIDC / OAuth2) and the Admin REST API for Keycloak behind one consistent facade, with hardened JWT validation and a full async mirror.

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.

1.0.3 is on PyPI — a bare pip install keycloak-sdk resolves it. A patch release on top of 1.0.2; no public API changed. Fixed: with expected_audience set to something other than the client id (a resource server), exchange_code with a nonce failed with invalid id_token, because the id_token was checked against the access token's expected audience. The id_token's aud is now checked for the client id (OIDC Core §2, §3.1.3.7): the exchange passes under the override, and an id_token whose aud lacks the client id is refused even when that aud is the override itself. validate() still applies the override to access tokens, sync and aio alike. Security: a failed auth call's error no longer carries tokens in its traceback's frame locals — str, repr and logging.exception never showed them, but TracebackException(capture_locals=True) and Sentry (which collects frame locals by default) did: the token response with its refresh token, the raw id_token, the code, code_verifier and refresh or introspection token passed in, the code_verifier an authorization_url generated before failing, and the client secret when building the lower client failed. Those paths and validate() now detach the SDK-internal frames and the cause chain's tracebacks and clear the arguments before raising the same exception — type, message and chain shape are unchanged; only the line numbers of SDK-internal frames are lost. ⚠️ The admin client now raises KeycloakAuthError (token response has no usable access_token) when its own client-credentials grant returns an empty, missing, null or non-string access_token, as the auth client already did, and sends no admin request — an empty one used to go out as Authorization: Bearer (sync) and the rest raised KeycloakTransportError. 1.0.0 was the first release carrying the stability guarantee: a breaking change to the public API requires a major bump.

⚠️ One breaking change since 0.1.0, and it is narrow (it landed in 0.2.0; 1.0.0 adds none): the keycloak_sdk.jwt module moved to keycloak_sdk._internal.jwt. Only code that imported JwtValidator from that path directly is affected — it was never in __all__ and appears in no quickstart. The normal validation path, kc.auth.validate(token), is unchanged. The move was structural: py.typed makes every module's signatures part of the public type surface, so a module under the top level was publishing joserfc's KeySet as part of this SDK's API.

Requirements

  • Python 3.10+
  • Ships the PEP 561 py.typed marker, so consumers can type-check with mypy too

Install

pip install keycloak-sdk

The distribution name is keycloak-sdk; the import package is keycloak_sdk.

Quickstart

from keycloak_sdk import KeycloakClient, KeycloakConfig

config = KeycloakConfig(
    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
)

# The `with` block closes both sessions on exit — the auth session and, since the admin facade owns its own `requests.Session` pair, those too (`AdminClient.close()` is not a no-op).
with KeycloakClient.create(config) as kc:
    # 1) Issue a client-credentials token. repr(TokenSet) masks every token value.
    token = kc.auth.client_credentials_token()

    # 2) Validate it — algorithm pinning, exact iss, aud containment, mandatory exp, clock skew.
    validated = kc.auth.validate(token.access_token)
    print(f"subject={validated.subject} aud={validated.audience}")

    # 3) Admin API — admin is created lazily on first access. create() returns the new user id.
    user_id = kc.admin.users.create({"username": "alice", "enabled": True})
    users = kc.admin.users.search(first=0, max=20)

validate() expects the token's aud to contain client_id by default, but a stock realm does not put the client id into a client-credentials token. Either set expected_audience="my-api" on the config to check the audience your tokens actually carry, or add an audience mapper to the client in Keycloak (Client scopes → dedicated scope → Add mapper → Audience). expected_audience applies to access tokens only: the id_token that exchange_code(..., nonce=...) verifies must always carry client_id in aud (OIDC Core §3.1.3.7).

Async

keycloak_sdk.aio is a complete async mirror — same method names (the sync close() is aclose() here), value types, and exceptions — so it never blocks the event loop (FastAPI and friends):

from keycloak_sdk import KeycloakConfig
from keycloak_sdk.aio import AsyncKeycloakClient


async def handler(config: KeycloakConfig) -> None:
    async with AsyncKeycloakClient.create(config) as kc:
        token = await kc.auth.client_credentials_token()
        validated = await kc.auth.validate(token.access_token)
        users = await kc.admin.users.search(first=0, max=20)

Only authorization_url stays synchronous — it assembles a URL and needs no network.

Security defaults

The SDK replaces the unsafe library defaults rather than inheriting them:

  • Algorithm pinning — the header-supplied alg is never trusted, so alg: none and HS/RS confusion are rejected structurally: joserfc decodes against the configured allowlist, and an empty allowlist is refused at construction rather than falling back to joserfc's permissive default set.
  • Strict claim checks — exact iss match, aud containment, mandatory exp, nbf enforced when present, 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_seconds, 30s by default) — so once the key set has been fetched, no volume of forged tokens makes the SDK issue more than one JWKS request per interval. A failed fetch is bounded separately: consecutive failures back off exponentially (0.2s, doubling, capped at 5s, with jitter), and inside that window the SDK fails fast without contacting the IdP. So a cold cache during an IdP outage no longer turns every validation into a request (measured: 20 attempts → 1 request, down from 20; the sync and async clients share the same state machine). ⚠️ The SDK never sleeps — it returns the error immediately, so retry pacing stays the caller's decision.
  • Secret handling — repr() of the config and token types masks secrets and tokens as *** (no prefix leak), and TLS verification is on by default.

Masking covers this SDK's own repr(); it cannot cover what your logging framework or a traceback does with a value you hand it. Python has no erasable string type, so the client secret lives in an ordinary str for its lifetime — masking is defence in depth, not an erasure guarantee.

Versioning and support

This SDK is 1.0 and follows SemVer: a breaking change to the public API requires a major bump. That promise is machine-backed — CI diffs this lane's public API against the previously published artifact on every build (griffe check), and a removal or an incompatible change fails the build. ⚠️ The gate compares the API surface. A change that leaves the surface identical but alters behaviour is not caught by it, so read the release notes before upgrading.

Only the newest released version of each language SDK receives security fixes; there are no long-term-support lines and older releases are not backported to.

Each of the nine languages versions independently. All nine reached 1.0.0 in the same release wave because they earned the same guarantee at the same time — they do not move in lockstep afterwards.

Documentation

License

Apache-2.0

Metadata

Release files for keycloak-sdk 1.0.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for keycloak-sdk 1.0.3
File Size Uploaded
keycloak_sdk-1.0.3.tar.gz 162.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for keycloak-sdk 1.0.3
File Interpreter ABI Platform
keycloak_sdk-1.0.3-py3-none-any.whl Python 3 none any Details

Total release size: 230.8 kB

Release files / keycloak_sdk-1.0.3.tar.gz

Download URL keycloak_sdk-1.0.3.tar.gz
Size 162.6 kB
Tags Source
SHA-256 checksum
How to use checksums
517236a8829b2f1f9b105bbae51f3d2077a2ae04cfb644eddb8da9b45f225d9c
BLAKE2b-256 checksum
How to use checksums
d1e8a40e4beb53829526a90c6441268dc3dda35109b1614927dd4747ab40151b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release files / keycloak_sdk-1.0.3-py3-none-any.whl

Download URL keycloak_sdk-1.0.3-py3-none-any.whl
Size 68.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1fb7bc96260a81b2f15d1048d5fbee2076c7607128ca4e6ea3cd2fa027b48b99
BLAKE2b-256 checksum
How to use checksums
dba1f52e106d393bb8dbb3a019aa9ee60995294a48ccc2df507ec24d9e899e55
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release history Release notifications | RSS feed

1.0.4

2 release files

This release

1.0.3 This release

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page