tai-keycloak
Keycloak para servicios Python, y las herramientas para levantarlo y desplegarlo.
- Validar tokens sin hablar con Keycloak: firma contra el JWKS del realm (cacheado, con rotación de claves), emisor, tipo, audiencia y caducidad.
- Login, refresco y logout en nombre de un usuario.
- Administrar usuarios, grupos, roles, clientes y el perfil de usuario con un coste conocido por operación, sin N+1.
tai-kc: Keycloak de desarrollo con Docker, un realm seguro por defecto y los ficheros para desplegarlo en cualquier orquestador, en Azure u on-premise con Traefik.
Es la pieza de la plataforma tai que usan las APIs que genera tai-api en modo Keycloak, y funciona igual de bien sola.
pip install tai-keycloak
Python 3.10+. El runtime depende de httpx, jwcrypto y pydantic.
En una API
from tai_keycloak import InvalidTokenError, Keycloak, KeycloakSettings
kc = Keycloak(KeycloakSettings.from_env()) # no abre conexiones
realm = kc.realm("main")
async def current_user(authorization: str):
try:
claims = await realm.tokens.validate(authorization.removeprefix("Bearer "))
except InvalidTokenError as error:
raise HTTPException(401, error.message)
return claims.preferred_username, claims.client_roles("api")
# al apagar
await kc.aclose()
validate() solo habla con Keycloak la primera vez (descarga el JWKS) y cuando llega un token
firmado con una clave nueva; el resto es CPU.
Configuración
| Variable | Qué es |
|---|---|
MAIN_KEYCLOAK_URL |
URL del servidor (https://auth.example.com, http://localhost:8090, con ruta si la tiene) |
KEYCLOAK_API_CLIENT_SECRET |
Secreto del cliente api: login y cuenta de servicio para administrar |
KEYCLOAK_PUBLIC_URL |
URL con la que los clientes ven Keycloak, si no es la misma (emisor de los tokens) |
KEYCLOAK_ADMIN_USERNAME / KEYCLOAK_ADMIN_PASSWORD |
Administrador de master, solo para gestionar realms |
KEYCLOAK_API_CLIENT / KEYCLOAK_APP_CLIENT |
Nombres de los clientes (por defecto api y app) |
KEYCLOAK_VERIFY_SSL, KEYCLOAK_TIMEOUT |
Verificación del certificado y segundos por petición |
No hay credenciales por defecto: si falta algo, el error dice qué definir.
Errores
Toda operación devuelve lo que promete o lanza un TaiKeycloakError con message,
solution y status (el código HTTP con el que respondería una API):
| Excepción | status | Cuándo |
|---|---|---|
InvalidTokenError |
401 | Token caducado, manipulado, de otro emisor, sin la audiencia… |
InvalidCredentialsError |
401 | Usuario o contraseña incorrectos (sin distinguir cuál) |
NotFoundError / ConflictError |
404 / 409 | No existe / ya existe o el nombre es ambiguo |
InvalidRequestError |
400 | Keycloak rechaza los datos (política de contraseñas, perfil…) |
PermissionDeniedError |
403 | A la cuenta de servicio le falta un rol |
ServiceAuthenticationError, ConfigurationError |
500 | Secreto o configuración incorrectos |
KeycloakUnavailableError / KeycloakServerError |
503 / 502 | Keycloak no responde / falla |
Administrar
from pydantic import SecretStr
from tai_keycloak import GroupCreate, UserCreate, UserUpdate
await realm.groups.create(GroupCreate(name="operario", client_roles={"api": ["autor-read"]}))
await realm.users.create(UserCreate(username="ana", password=SecretStr("…"), groups=["operario"]))
page = await realm.users.list(group="operario", enabled=True, limit=50) # items + total
ana = await realm.users.get("ana", effective=True) # roles efectivos, heredados de sus grupos
await realm.users.update("ana", UserUpdate(password=SecretStr("…"))) # cierra sus sesiones
Lo que conviene saber:
- Crear es atómico: si un grupo o un rol no existe, el usuario no queda creado.
UserUpdatesolo toca lo que se indica:attributesse mezcla; grupos y roles son el conjunto final.- Cambiar la contraseña o deshabilitar a un usuario cierra todas sus sesiones.
- Keycloak 26 descarta en silencio los atributos que el perfil no declara; la librería lo
detecta y lanza el error. Los atributos de RLS se declaran con
realm.profile.set_rls_attributes([...]).
En scripts y notebooks, la misma API bloqueante:
from tai_keycloak.sync import SyncKeycloak
with SyncKeycloak() as kc:
print(kc.realm("main").users.count())
Y para probar código que valida tokens sin un Keycloak, tai_keycloak.testing.SigningKeys
firma tokens como los de Keycloak y sirve su JWKS con un httpx.MockTransport.
tai-kc
tai-kc init --mode development # keycloak/: Dockerfile, compose, realm seguro, .env con secretos aleatorios
tai-kc run # Keycloak en http://localhost:8090
tai-kc check # ¿puede tu API hablar con él? dice qué falla y cómo arreglarlo
tai-kc realm new norte # otro realm
tai-kc api users --group operario
tai-kc stop
Modos de init:
| Modo | Qué genera |
|---|---|
development |
compose con H2 en un volumen (o PostgreSQL con tai-kc run --db postgres) |
production |
la imagen prod optimizada y las variables que necesita, para cualquier orquestador |
azure |
lo anterior más un workflow de GitHub Actions que construye, sube a ACR y despliega en una Web App |
onpremise |
compose con Traefik (TLS con tus certificados) delante de Keycloak y PostgreSQL externo |
Ningún secreto entra en una imagen: contraseñas y secretos se pasan en runtime. El realm
generado no tiene redirecciones comodín, protege contra fuerza bruta, exige contraseñas de 12
caracteres, deja nombre y apellidos opcionales y da a la cuenta de servicio de la API solo los
roles que necesita. init nunca borra nada, y un .env existente no se pisa ni con --force.
Desarrollo
pip install -e ".[dev]"
pytest # sin Keycloak: se saltan los de integración
TAI_KEYCLOAK_TEST_URL=http://admin:admin@localhost:8080 pytest # con un Keycloak real
Las reglas de diseño de la librería están en .claude/rules/.
Release files for tai-keycloak 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tai_keycloak-0.3.1.tar.gz | 71.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tai_keycloak-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 164.8 kB
Release files / tai_keycloak-0.3.1.tar.gz
| Download URL | tai_keycloak-0.3.1.tar.gz |
|---|---|
| Size | 71.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0f60983f0656a1024b8db33fe97863c51da07894b492a28a1352b0c0d61d4bae
|
|
BLAKE2b-256 checksum How to use checksums |
ae3b3da0bf05a52b98199c30dd6574420211b0a4e0c963c3e7aa5b3196a601ae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.5.1 CPython/3.12.14 Linux/6.17.0-1022-azure
|
Release files / tai_keycloak-0.3.1-py3-none-any.whl
| Download URL | tai_keycloak-0.3.1-py3-none-any.whl |
|---|---|
| Size | 92.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4fb34f53072171b074df74489d8157bdd0ae3294e633dc43ee4791db47dd4aae
|
|
BLAKE2b-256 checksum How to use checksums |
f740da248a25b76ed999980984708012488f67465505ee36b4646f3f41b23d68
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.5.1 CPython/3.12.14 Linux/6.17.0-1022-azure
|