Skip to main content

Async Keycloak client with automated token management

Project description

PyKeycloak

PyKeycloak is a library for working with Keycloak that provides asynchronous methods for authentication, token management, and permission handling.

What's Different from Other Libraries

  • Sanitized logging: Automatically hide sensitive data in request/response logs.
  • Httpx-powered: Gain full control using standard httpx client configuration.
  • Rich Request/Response handling: Access a comprehensive list of parameters and detailed response fields.
  • Flexible Data Access: Easily work with both raw data and structured representations.
  • Environment-based config: Quick setup using environment variables.

Installation

To install dependencies for local development, use the following command:

make install

Development and Security Tooling

Runtime users of the library only install pykeycloak and its package dependencies.

For contributors (local checks + CI parity), install:

  • uv
  • pre-commit

Then run:

make install
uv run pre-commit install

Security checks are executed in both pre-commit and GitHub Actions:

  • Dependency CVE audit: pip-audit --strict

Release Version Bump

Releases are tag-driven via GitHub Actions:

  • Tag format: vX.Y.Z (example: v0.7.4)
  • On tag push, CI syncs pyproject.toml version from the tag before build/publish.
  • After successful publish, GitHub Release is created automatically with generated release notes.
  • Build artifacts include a CycloneDX SBOM (sbom.cyclonedx.json) attached to the GitHub Release.
  • CI enforces an SBOM license deny policy (fails on disallowed copyleft licenses by default).
  • License policy is defined in .license-policy.toml (deny = [...]).

Local helpers:

make release-bump                    # uses GITHUB_REF_NAME
make release-bump-tag TAG=v0.7.4     # explicit tag

Usage Examples

The library can be used in 3 different ways:

  1. Make requests directly through the client
  2. Use the provider to get a response with content
  3. Use the service to get either raw responses or Representation objects corresponding to the data received from Keycloak

Constants

Dynamic environment

## These variables are dependant on client name
##
## KEYCLOAK_REALM_{realm_client_name}_REALM_NAME
##
## When the instance attached to container it looking for environment variables
## KEYCLOAK_REALM_{realm_client_name}_CLIENT_ID
## KEYCLOAK_REALM_{realm_client_name}_CLIENT_SECRET
##
## pykeycloak_client.register(key, RealmClient.from_env(client_name=realm_client_name))
##
## But you don't need those when making RealmClient not from env
##
KEYCLOAK_REALM_OTAGO_SERVICE_REALM_NAME=
KEYCLOAK_REALM_OTAGO_SERVICE_CLIENT_UUID=
KEYCLOAK_REALM_OTAGO_SERVICE_CLIENT_ID=
KEYCLOAK_REALM_OTAGO_SERVICE_CLIENT_SECRET=

KEYCLOAK_REALM_OTAGO_SSO_REALM_NAME=
KEYCLOAK_REALM_OTAGO_SSO_CLIENT_UUID=
KEYCLOAK_REALM_OTAGO_SSO_CLIENT_ID=
KEYCLOAK_REALM_OTAGO_SSO_CLIENT_SECRET=
##

Default environment

KEYCLOAK_BASE_URL=
KEYCLOAK_HTTPX_CLIENT_PARAMS_HTTP1=
KEYCLOAK_HTTPX_CLIENT_PARAMS_HTTP2=
KEYCLOAK_HTTPX_CLIENT_PARAMS_SSL_VERIFY=
KEYCLOAK_HTTPX_CLIENT_PARAMS_FOLLOW_REDIRECTS=
KEYCLOAK_HTTPX_CLIENT_PARAMS_TRUST_ENV=
KEYCLOAK_HTTPX_CLIENT_PARAMS_TIMEOUT=
KEYCLOAK_HTTPX_CLIENT_PARAMS_MAX_CONNECTIONS=
KEYCLOAK_HTTPX_CLIENT_PARAMS_MAX_KEEPALIVE_CONNECTIONS=
KEYCLOAK_HTTPX_CLIENT_PARAMS_KEEPALIVE_EXPIRY=
KEYCLOAK_HTTPX_CLIENT_PARAMS_MAX_REDIRECTS=
KEYCLOAK_HTTPX_CLIENT_PARAMS_DEFAULT_ENCODING=utf-8
KEYCLOAK_MAX_ROWS_QUERY_LIMIT=1000

KEYCLOAK_HTTPX_HTTP_TRANSPORT_HTTP_VERIFY=
KEYCLOAK_HTTPX_HTTP_TRANSPORT_HTTP_CERT=
KEYCLOAK_HTTPX_HTTP_TRANSPORT_HTTP_TRUST_ENV=
KEYCLOAK_HTTPX_HTTP_TRANSPORT_HTTP_HTTP1=
KEYCLOAK_HTTPX_HTTP_TRANSPORT_HTTP_HTTP2=
KEYCLOAK_HTTPX_HTTP_TRANSPORT_HTTP_RETRIES=
KEYCLOAK_HTTPX_HTTP_TRANSPORT_HTTP_PROXY=
KEYCLOAK_HTTPX_HTTP_TRANSPORT_HTTP_UDS=
KEYCLOAK_HTTPX_HTTP_TRANSPORT_HTTP_LOCAL_ADDRESS=
KEYCLOAK_HTTPX_HTTP_TRANSPORT_HTTP_MAX_CONNECTIONS=
KEYCLOAK_HTTPX_HTTP_TRANSPORT_HTTP_KEEPALIVE_EXPIRY=
KEYCLOAK_HTTPX_HTTP_TRANSPORT_HTTP_MAX_KEEPALIVE_CONNECTIONS=

KEYCLOAK_HTTP_RETRY_ENABLED=true
KEYCLOAK_HTTP_RETRY_MAX_ATTEMPTS=3
KEYCLOAK_HTTP_RETRY_BASE_DELAY_SECONDS=0.2
KEYCLOAK_HTTP_RETRY_MAX_DELAY_SECONDS=2.0
KEYCLOAK_HTTP_RETRY_JITTER_SECONDS=0.1
KEYCLOAK_HTTP_RETRY_METHODS=GET,HEAD,OPTIONS,DELETE

DATA_SANITIZER_EXTRA_SENSITIVE_KEYS=

UMA_PERMISSIONS_CHUNK_SIZE=1

Initial start

from pykeycloak_client.pykeycloak import PyKeycloak
from pykeycloak_client.core.realm import RealmClient

key = "otago_service"
pkc = PyKeycloak()

pkc.register(key, RealmClient.from_env(client_name=key))
pkc.get(key)

Providers

  • KeycloakInMemoryProviderAsync - Asynchronous provider for working with Keycloak that provides methods for authentication, token refresh, user information retrieval, logout, token introspection, device authentication, and certificate retrieval.

Services

  • AuthService - authentication, token refresh, user information retrieval, logout, token introspection, device authentication, and certificate retrieval.
  • UmaService - UMA permissions.
  • UsersService
  • RolesService
  • SessionsService
  • ClientsService
  • AuthzService
  • AuthzResourceService
  • AuthzScopeService
  • AuthzPermissionService
  • AuthzPolicyService
  • WellKnownService

Core Entities

Payloads

  • TokenIntrospectionPayload - Payload for token introspection containing the token.

  • RTPIntrospectionPayload - Payload for token introspection inherited from TokenIntrospectionPayload, containing the token type.

  • ObtainTokenPayload - Base class for obtaining a token, containing the scope and grant type.

  • UserCredentialsLoginPayload - Payload for user authentication containing username and password.

  • ClientCredentialsLoginPayload - Payload for client authentication used to obtain a client token.

  • RefreshTokenPayload - Payload for refreshing a token containing the refresh token.

  • UMAAuthorizationPayload - Payload for UMA authorization containing audience, permissions, and other parameters.

Representations

Representations duplicate the data from Keycloak documentation based on the actual values they return.

TokenRepresentation - Representation of a token containing information about the access token, expiration time, scope, and token type.

UserInfoRepresentation - Representation of user information containing user data such as first name, last name, email address, and other attributes.

RealmAccessRepresentation - Representation of realm access containing user roles in the realm.

IntrospectRepresentation - Representation of token introspection result containing token information such as audience, expiration time, token type, and other attributes.

Client

RealmClient - Entity that stores realm data:

import os
from pykeycloak_client.core.realm import RealmClient

## To get pre-configured client based on environment variables
RealmClient.from_env(client_name='random_client_name')

# or if you hande environment variables manually
RealmClient(
    realm_name='realm_name',
    client_id=os.getenv("KEYCLOAK_REALM_CLIENT_ID"),
    client_uuid=os.getenv("KEYCLOAK_REALM_CLIENT_UUID"),
    client_secret=os.getenv("KEYCLOAK_REALM_CLIENT_SECRET")
)

Sanitizer

Processes headers and request/response logs, hiding all critical information and marking it as hidden.

import os
from pykeycloak_client.core.sanitizer import SensitiveDataSanitizer

SensitiveDataSanitizer.from_env()

SensitiveDataSanitizer(
    sensitive_keys=frozenset(os.getenv("DATA_SANITIZER_EXTRA_SENSITIVE_KEYS", None))
)

Client Initialization

To get started, you need to initialize the client using environment variables:

User Authentication

To authenticate a user, use the user_login_async method:

from pykeycloak_client.providers.payloads import UserCredentialsLoginPayload
from pykeycloak_client.pykeycloak import PyKeycloak

pkc = PyKeycloak()

# add client ....

token = await pkc.get('otago_client').auth.user_login_async(
    payload=UserCredentialsLoginPayload(
        username=username,
        password=password,
    ))

Token Refresh

To refresh a token, use the refresh_token_async method:

from pykeycloak_client.pykeycloak import PyKeycloak

pkc = PyKeycloak()

# add client ....

token = await pkc.get('otago_client').auth.refresh_token_async(
    payload=RefreshTokenPayload(refresh_token=token.refresh_token)
)

Integration Smoke Tests

Integration tests are disabled by default and run only against a real Keycloak instance.

Required environment variables:

PYKEYCLOAK_INTEGRATION_ENABLED=1
KEYCLOAK_BASE_URL=http://localhost:8080
KEYCLOAK_REALM_IT_REALM_NAME=master
KEYCLOAK_REALM_IT_CLIENT_UUID=<client-uuid>
KEYCLOAK_REALM_IT_CLIENT_ID=<client-id>
KEYCLOAK_REALM_IT_CLIENT_SECRET=<client-secret>

Optional variables for user login + UMA smoke test:

KEYCLOAK_IT_USERNAME=<username>
KEYCLOAK_IT_PASSWORD=<password>
KEYCLOAK_IT_UMA_PERMISSIONS=/resource#view,/resource#update

Run:

uv run pytest tests/integration -m integration -vv -s

CI has a manual compatibility matrix against Keycloak 24.0, 25.0, and 26.0 via GitHub Actions workflow_dispatch input (run_integration_matrix=true).

Token Introspection

To introspect a token, use the introspect_async method:

from pykeycloak_client.providers.payloads import TokenIntrospectionPayload
from pykeycloak_client.pykeycloak import PyKeycloak

pkc = PyKeycloak()

# add client ....

introspection = await pkc.get('otago_client').auth.introspect_token_async(
    payload=TokenIntrospectionPayload(
        token=refresh.auth_token,
    )
)

UMA Permission Retrieval

To retrieve UMA permissions, use the get_uma_permissions_async method:

from pykeycloak_client.providers.payloads import UMAAuthorizationPayload
from pykeycloak_client.pykeycloak import PyKeycloak

pkc = PyKeycloak()

# add client ....

permissions = await pkc.get('otago_client').uma.get_uma_permissions_async(
    payload=UMAAuthorizationPayload(
        audience=client.client_id,
        subject_token=token.auth_token,  # user token
        permissions=['otago/users#view']
    )
)

User Information Retrieval

To retrieve user information, use the get_user_info_async method:

from pykeycloak_client.pykeycloak import PyKeycloak

pkc = PyKeycloak()

# add client ....

user_info = await pkc.get('otago_client').auth.get_user_info_async(
    access_token=refresh.auth_token
)

Logout

To log out, use the logout_async method:

from pykeycloak_client.pykeycloak import PyKeycloak

pkc = PyKeycloak()

# add client ....

await pkc.get('otago_client').auth.logout_async(refresh.refresh_token)

Certificate Retrieval

To retrieve certificates, use the get_certs_async method:

from pykeycloak_client.pykeycloak import PyKeycloak

pkc = PyKeycloak()

# add client ....

certs = await pkc.get('otago_client').well_known.get_certs_async()

Other Methods

All services are available in protocols.py with their methods.

License

This project is licensed under the MIT License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pykeycloak_client-0.8.0.tar.gz (39.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pykeycloak_client-0.8.0-py3-none-any.whl (55.4 kB view details)

Uploaded Python 3

File details

Details for the file pykeycloak_client-0.8.0.tar.gz.

File metadata

  • Download URL: pykeycloak_client-0.8.0.tar.gz
  • Upload date:
  • Size: 39.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pykeycloak_client-0.8.0.tar.gz
Algorithm Hash digest
SHA256 d3d3f2f4c0b3cf9abab2e3f677c4bc1c148e5dd35bf3241f37d11227a23a1c74
MD5 ee32badfa551235fd0ee0d2e78328bfb
BLAKE2b-256 af2d4e021d3604cb55d3298beb36dfdae7ebc0dc04c6b4d7dc20e778e60e5d28

See more details on using hashes here.

File details

Details for the file pykeycloak_client-0.8.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pykeycloak_client-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 669114e9108f77527165e6d3470268e48abb576df739d34719299813fc035ec4
MD5 625d2fd28cda7bf05304790341ee00aa
BLAKE2b-256 04408ae943dfae34e606490f7fbabab4a631fb2e63f62234845b8be6f8775319

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page