No project description provided
Project description
🔐 tai-keycloak
Framework completo de desarrollo para Keycloak: Cliente Python elegante, herramientas CLI y recursos de deployment unificados.
📋 Tabla de Contenidos
- Características Principales
- Instalación
- Inicio Rápido
- Arquitectura de la Librería
- Guía de Uso Detallada
- Deployment
- Arquitectura y Patrones de Diseño
- Ejemplos Completos
- Troubleshooting
✨ Características Principales
🔧 Service Layer Completo
- Cliente Admin para operaciones administrativas
- Cliente App para flujos OIDC (login, tokens, validación)
- DAOs especializados para cada entidad (Users, Groups, Roles, Clients)
- DTOs tipados con Pydantic para validación automática
- Manejo de errores robusto con
OperationResult - Soporte async/sync para operaciones concurrentes
🛠️ CLI Tools (tai-kc)
tai-kc init- Inicialización de proyecto con recursos de Keycloaktai-kc run- Levantar Keycloak con profiles (development, production, on-premise)tai-kc api user- Gestión de usuarios desde CLItai-kc api client- Gestión de clientestai-kc api role- Gestión de rolestai-kc api realm- Gestión de realms
🚀 Docker Resources Unificados
- Dockerfile multi-stage para todos los entornos
- Docker Compose con profiles para desarrollo, Azure y on-premise
- Variables de entorno transparentes (sin scripts opacos)
- GitHub Actions workflow para CI/CD en Azure
- Traefik integrado para HTTPS en on-premise
📦 Instalación
pip install tai-keycloak
Dependencias
- Python 3.10+
- Docker 20.10+ (opcional, para deployment local)
- Docker Compose 2.0+ (opcional)
🚀 Inicio Rápido
1. Configuración Rápida
La librería detecta automáticamente la configuración desde variables de entorno:
# Configuración completa
export MAIN_KEYCLOAK_URL=admin:secret@keycloak.company.com:8080
# Solo usuario (password por defecto: 'admin')
export MAIN_KEYCLOAK_URL=myuser@keycloak.company.com:8080
# Solo host y puerto (user/password por defecto: 'admin/admin')
export MAIN_KEYCLOAK_URL=keycloak.company.com:8080
# Solo host - detección inteligente de puerto/protocolo
export MAIN_KEYCLOAK_URL=localhost # → http://localhost:8090
export MAIN_KEYCLOAK_URL=myapp.azurewebsites.net # → https://myapp.azurewebsites.net
export MAIN_KEYCLOAK_URL=keycloak.company.com # → https://keycloak.company.com
2. Primer Script
from tai_keycloak import kc, User
# Crear usuario
new_user = User(
username="john.doe",
email="john@example.com",
firstName="John",
lastName="Doe",
password="SecurePass123!"
)
result = kc.admin.user.create(new_user)
if result.success:
print(f"✅ Usuario creado: {result.data.username}")
else:
print(f"❌ Error: {result.message}")
3. Levantar Keycloak Localmente
# Inicializar proyecto con recursos Docker
tai-kc init --mode development
# Levantar Keycloak
cd keycloak/
tai-kc run
# Acceder a http://localhost:8090
🏗️ Arquitectura de la Librería
La librería está organizada en 3 capas principales:
tai_keycloak/
├── service/ # Capa de servicio (API Python)
├── cli/ # Herramientas de línea de comandos
└── docker/ # Recursos de deployment
1. Service Layer (tai_keycloak.service)
La capa de servicio proporciona una API Python elegante y tipada para interactuar con Keycloak.
Arquitectura del Service Layer
service/
├── clients/ # Clientes principales (Admin, App, API)
│ ├── admin.py # KeycloakAdminClient (operaciones administrativas)
│ ├── app.py # KeycloakAppClient (flujos OIDC)
│ ├── api.py # KeycloakAPIClient (API REST)
│ └── config.py # KeycloakConfig (configuración)
├── daos/ # Data Access Objects (CRUD por entidad)
│ ├── user/ # UserDAO (sync + async)
│ ├── group/ # GroupDAO (sync + async)
│ ├── client.py # ClientDAO
│ ├── realm.py # RealmDAO
│ ├── client_roles.py # ClientRoleDAO
│ ├── realm_roles.py # RealmRoleDAO
│ └── profile.py # UsersProfileDAO
├── dtos/ # Data Transfer Objects (modelos Pydantic)
│ ├── entity.py # User, Group, Role, Client, etc.
│ ├── response.py # OperationResult, ErrorResult
│ ├── token.py # Token, AccessToken
│ └── base.py # PrettyModel (base para DTOs)
└── token/ # Gestión de tokens JWT
├── syn.py # TokenDAO (sync)
└── asyn.py # TokenDAO (async)
Componentes Clave
1.1 KeycloakAdminClient
Cliente principal para operaciones administrativas con Keycloak.
from tai_keycloak import kc
# Singleton pattern - instancia única configurada
admin = kc.admin
# Acceso a DAOs
admin.user # UserDAO
admin.group # GroupDAO
admin.client # ClientDAO
admin.realm # RealmDAO
admin.app_role # ClientRoleDAO (cliente 'app')
admin.api_role # ClientRoleDAO (cliente 'api')
admin.realm_role # RealmRoleDAO
Características:
- ✅ Singleton pattern - Una sola conexión autenticada
- ✅ Lazy initialization - Conexión bajo demanda
- ✅ Auto-autenticación - Manejo automático de tokens
- ✅ Cambio de realm - Trabaja con realm 'main' por defecto
1.2 KeycloakAppClient
Cliente OIDC para flujos de autenticación tipo "human-like".
from tai_keycloak import kc
app = kc.app
# Obtener clave pública para validar tokens
public_key = app.public_key
# Validar token
from tai_keycloak.service.token import TokenDAO
result = TokenDAO.decode(
token="eyJhbGc...",
key=public_key,
expected_audience="app",
expected_issuer="https://keycloak.company.com/realms/main"
)
if result.success:
access_token = result.data
print(f"Usuario: {access_token.preferred_username}")
print(f"Roles: {access_token.realm_access.roles}")
Características:
- ✅ Validación de tokens JWT con jwcrypto
- ✅ Verificación de firma con clave pública del realm
- ✅ Validación de exp, iss, aud automática
- ✅ DTOs tipados para claims del token
1.3 DAOs (Data Access Objects)
Cada entidad de Keycloak tiene su propio DAO con operaciones CRUD.
UserDAO
from tai_keycloak import kc, User
# Crear usuario
new_user = User(
username="jane.smith",
email="jane@example.com",
firstName="Jane",
lastName="Smith",
password="MySecurePass123!",
attributes={
"department": "Engineering",
"location": "Remote"
}
)
result = kc.admin.user.create(new_user)
# Obtener usuario
result = kc.admin.user.get("jane.smith")
user = result.data
# Listar usuarios
result = kc.admin.user.list(limit=50, offset=0)
users = result.data
# Actualizar usuario
update = User(email="jane.new@example.com")
result = kc.admin.user.update("jane.smith", update)
# Eliminar usuario
result = kc.admin.user.delete("jane.smith")
# Asignar rol de cliente
result = kc.admin.user.assign_client_role(
username="jane.smith",
client_name="api",
role_name="admin"
)
# Asignar rol de realm
result = kc.admin.user.assign_realm_role(
username="jane.smith",
role_name="realm-admin"
)
# Agregar a grupo
result = kc.admin.user.add_to_group(
username="jane.smith",
group_name="Developers"
)
Métodos disponibles:
create(user: User) -> OperationResult[User]get(username: str) -> OperationResult[User]list(limit: int, offset: int) -> OperationResult[List[User]]update(username: str, payload: User) -> OperationResult[User]delete(username: str) -> OperationResult[None]assign_client_role(username, client_name, role_name)assign_realm_role(username, role_name)add_to_group(username, group_name)remove_from_group(username, group_name)
GroupDAO
from tai_keycloak import kc, Group
# Crear grupo
new_group = Group(
name="Engineering",
attributes={
"department": ["IT"],
"cost_center": ["1000"]
}
)
result = kc.admin.group.create(new_group)
# Obtener grupo
result = kc.admin.group.get("Engineering")
group = result.data
# Listar grupos
result = kc.admin.group.list()
groups = result.data
# Actualizar grupo
update = Group(attributes={"location": ["Remote"]})
result = kc.admin.group.update("Engineering", update)
# Eliminar grupo
result = kc.admin.group.delete("Engineering")
ClientRoleDAO
from tai_keycloak import kc, Role
# Crear rol para cliente 'api'
new_role = Role(
name="api-admin",
description="Admin del API"
)
result = kc.admin.api_role.create(new_role)
# Obtener rol
result = kc.admin.api_role.get("api-admin")
role = result.data
# Listar roles del cliente 'api'
result = kc.admin.api_role.list()
roles = result.data
# Para cliente 'app'
result = kc.admin.app_role.create(Role(name="app-viewer"))
RealmRoleDAO
from tai_keycloak import kc, Role
# Crear rol de realm
new_role = Role(
name="realm-manager",
description="Manager de realm"
)
result = kc.admin.realm_role.create(new_role)
# Listar roles de realm
result = kc.admin.realm_role.list()
roles = result.data
ClientDAO
from tai_keycloak import kc, Client
# Obtener cliente
result = kc.admin.client.get("api")
client = result.data
# Listar clientes
result = kc.admin.client.list()
clients = result.data
1.4 DTOs (Data Transfer Objects)
Todos los modelos están basados en Pydantic para validación automática.
User
from tai_keycloak import User
user = User(
username="john.doe", # Requerido
email="john@example.com", # Validación de email
firstName="John",
lastName="Doe",
password="secret", # No se devuelve en get()
enabled=True, # Default: True
emailVerified=True, # Default: True
attributes={ # Atributos custom
"department": "Sales",
"employee_id": "12345"
},
groups=["Sales", "Managers"], # Nombres de grupos
realmRoles=["user"], # Roles de realm
clientRoles={ # Roles de clientes
"api": ["read", "write"],
"app": ["viewer"]
}
)
Group
from tai_keycloak import Group
group = Group(
name="Developers", # Requerido
path="/Developers", # Path completo
attributes={
"department": ["Engineering"],
"location": ["Remote", "Office"]
},
realmRoles=["developer"],
clientRoles={
"api": ["developer-api"]
},
subGroups=[ # Grupos anidados
Group(name="Backend"),
Group(name="Frontend")
]
)
Role
from tai_keycloak import Role
role = Role(
name="admin", # Requerido
description="Administrator role",
composite=False, # ¿Es role compuesto?
clientRole=True, # ¿Es role de cliente?
containerId="client-uuid",
attributes={
"permissions": ["read", "write", "delete"]
}
)
Client
from tai_keycloak import Client, ClientProtocol, AccessType
client = Client(
clientId="my-app", # Requerido
name="My Application",
description="Application client",
protocol=ClientProtocol.OPENID_CONNECT,
publicClient=False,
bearerOnly=False,
enabled=True,
redirectUris=[
"https://myapp.com/callback"
],
webOrigins=[
"https://myapp.com"
],
attributes={
"access.token.lifespan": "1800"
}
)
1.5 OperationResult Pattern
Todas las operaciones devuelven OperationResult para manejo consistente de errores:
from tai_keycloak import kc
result = kc.admin.user.create(new_user)
# Verificar éxito
if result.success:
user = result.data # User object
print(result.message) # "Usuario 'john.doe' creado exitosamente"
else:
error = result.error # ErrorResult object
print(error.message) # Mensaje de error
print(error.details) # Detalles técnicos
print(error.operation) # "User.create"
Estructura de OperationResult:
class OperationResult[T]:
success: bool # True si operación exitosa
message: str # Mensaje descriptivo
data: T | None # Datos resultado (si success=True)
error: ErrorResult | None # Error detallado (si success=False)
1.6 TokenDAO
Gestión de tokens JWT de Keycloak.
from tai_keycloak import kc
from tai_keycloak.service.token import TokenDAO
# Obtener clave pública del realm
public_key = kc.app.public_key
# Decodificar y validar token
result = TokenDAO.decode(
token="eyJhbGciOiJSUzI1NiIs...",
key=public_key,
expected_audience="api",
expected_issuer="https://keycloak.company.com/realms/main"
)
if result.success:
token = result.data
# Claims estándar
print(f"Sub: {token.sub}")
print(f"Username: {token.preferred_username}")
print(f"Email: {token.email}")
print(f"Name: {token.name}")
# Roles
print(f"Realm roles: {token.realm_access.roles}")
print(f"Client roles: {token.resource_access}")
# Expiración
print(f"Expires at: {token.exp}")
print(f"Issued at: {token.iat}")
Validaciones automáticas:
- ✅ Verificación de firma con clave pública
- ✅ Validación de expiración (
exp) - ✅ Validación de issuer (
iss) - ✅ Validación de audience (
aud)
2. CLI Tools (tai-kc)
Herramientas de línea de comandos para gestión de Keycloak.
Comandos Principales
tai-kc init
Inicializa un proyecto con recursos de Keycloak.
# Modo desarrollo (H2 in-memory)
tai-kc init --mode development
# Modo Azure (PostgreSQL + CI/CD)
tai-kc init --mode azure
# Modo on-premise (Traefik + HTTPS)
tai-kc init --mode onpremise
Qué hace:
- ✅ Crea carpeta
keycloak/en el CWD - ✅ Copia
Dockerfileydocker-compose.yml - ✅ Copia archivo
.envsegún el modo - ✅ Copia
main-realm.json(configuración precargada) - ✅ Copia GitHub Actions workflow (si
mode=azure) - ✅ Copia configuración de Traefik (si
mode=onpremise) - ✅ Muestra siguiente pasos
tai-kc run
Levanta Keycloak localmente con Docker Compose.
# Desarrollo básico (H2)
tai-kc run
# Desarrollo con PostgreSQL
tai-kc run --profile development-db
# On-premise con Traefik + HTTPS
tai-kc run --profile onpremise
# Rebuild forzado
tai-kc run --build
# Sin verificación de DB
tai-kc run --skip-db-check
Opciones:
--profile- Profile de docker-compose (development, development-db, onpremise)--build- Forzar rebuild de la imagen--skip-db-check- Omitir verificación de base de datos
Qué hace:
- ✅ Verifica que Docker esté corriendo
- ✅ Verifica conexión a base de datos (si aplica)
- ✅ Ejecuta
docker-compose up -dcon el profile - ✅ Espera a que Keycloak esté listo
- ✅ Muestra información de acceso
- ✅ Monitorea containers y maneja Ctrl+C
tai-kc api user
Gestión de usuarios desde CLI.
# Listar usuarios
tai-kc api user list
# Crear usuario (interactivo)
tai-kc api user create
# Eliminar usuario
tai-kc api user delete john.doe
tai-kc api client
Gestión de clientes.
# Listar clientes
tai-kc api client list
# Ver detalles de cliente
tai-kc api client get api
tai-kc api role
Gestión de roles.
# Listar roles de cliente
tai-kc api role client list --client api
# Listar roles de realm
tai-kc api role realm list
tai-kc api realm
Gestión de realms.
# Ver realm actual
tai-kc api realm get main
# Listar todos los realms
tai-kc api realm list
3. Docker Resources
Recursos unificados para deployment en múltiples entornos.
Estructura
docker/
├── Dockerfile # Multi-stage build (4 stages)
├── docker-compose.yml # Compose con profiles
├── main-realm.json # Realm precargado
├── .env.example # Template de variables
├── .env.development # Desarrollo local
├── .env.azure # Azure Web Apps
├── .env.onpremise # On-premise
├── .gitignore
├── README.md # Documentación detallada
├── workflows/
│ ├── deploy-azure.yml # GitHub Actions
│ └── GITHUB_SECRETS.md # Guía de secretos
└── traefik/
├── dynamic/
│ └── tls.yml # Config TLS
└── certs/
└── README.md # Guía certificados
Dockerfile Multi-Stage
4 stages para diferentes entornos:
# Stage 1: base
FROM quay.io/keycloak/keycloak:26.4.5 AS base
# Configuración común
# Stage 2: development
FROM base AS development
# H2 in-memory, desarrollo rápido
# Stage 3: production
FROM base AS production
# PostgreSQL externo, KC_PROXY=edge
# Stage 4: azure
FROM base AS azure
# Optimizado para Azure Web Apps
Docker Compose Profiles
Un solo archivo para todos los escenarios:
services:
keycloak:
profiles:
- development
- development-db
- production
- onpremise
# ...
postgres:
profiles:
- development-db
# PostgreSQL 16-alpine
traefik:
profiles:
- onpremise
# Traefik v3.1 + HTTPS
Variables de Entorno
Todas las variables son transparentes y documentadas:
# Deployment
DEPLOYMENT_MODE=development
KEYCLOAK_VERSION=26.4.5
# Admin credentials
KC_BOOTSTRAP_ADMIN_USERNAME=admin
KC_BOOTSTRAP_ADMIN_PASSWORD=admin
KEYCLOAK_API_CLIENT_SECRET=dev-secret
# Database
KC_DB=postgres
KC_DB_URL_HOST=localhost
KC_DB_URL_PORT=5432
KC_DB_URL_DATABASE=keycloak
KC_DB_USERNAME=keycloak_user
KC_DB_PASSWORD=keycloak_pass
# HTTP
KC_HTTP_PORT=8090
KC_LOG_LEVEL=INFO
# Hostname (production)
KC_HOSTNAME=https://keycloak.company.com
KC_HOSTNAME_PATH=/keycloak
KC_PROXY_HEADERS=xforwarded
Ver docker/README.md para documentación completa.
📖 Guía de Uso Detallada
Configuración
Opción 1: Variable de Entorno
export MAIN_KEYCLOAK_URL=admin:secret@keycloak.company.com:8080
Opción 2: Configuración Programática
from tai_keycloak import KeycloakConfig, KeycloakAdminClient
config = KeycloakConfig(
protocol="https",
hostname="keycloak.company.com",
port=443,
username="admin",
password="secret",
verify_ssl=True
)
admin = KeycloakAdminClient(config)
Opción 3: Configuración Parcial con Env
# Env: MAIN_KEYCLOAK_URL=keycloak.company.com:8080
# Sobrescribir solo username/password
from tai_keycloak import KeycloakConfig
config = KeycloakConfig(
username="custom_admin",
password="custom_pass"
)
# Hereda hostname/port de env
Gestión de Usuarios
Crear Usuario Completo
from tai_keycloak import kc, User
user = User(
username="alice.wonder",
email="alice@example.com",
firstName="Alice",
lastName="Wonder",
password="MySecurePass123!",
enabled=True,
emailVerified=True,
attributes={
"department": "Marketing",
"employee_id": "EMP-001",
"cost_center": "2000"
}
)
result = kc.admin.user.create(user)
if result.success:
print(f"✅ Usuario creado: {result.data.id}")
# Asignar a grupo
kc.admin.user.add_to_group("alice.wonder", "Marketing")
# Asignar rol de cliente
kc.admin.user.assign_client_role(
username="alice.wonder",
client_name="api",
role_name="viewer"
)
# Asignar rol de realm
kc.admin.user.assign_realm_role("alice.wonder", "user")
Buscar y Actualizar Usuario
# Buscar usuario
result = kc.admin.user.get("alice.wonder")
if result.success:
user = result.data
print(f"Email: {user.email}")
print(f"Grupos: {user.groups}")
print(f"Roles API: {user.clientRoles.get('api', [])}")
# Actualizar datos
update = User(
email="alice.new@example.com",
attributes={
"phone": "+1234567890"
}
)
result = kc.admin.user.update("alice.wonder", update)
Listar Usuarios con Paginación
# Primera página (50 usuarios)
result = kc.admin.user.list(limit=50, offset=0)
if result.success:
users = result.data
print(f"Total: {len(users)} usuarios")
for user in users:
print(f"- {user.username} ({user.email})")
Gestión de Grupos
Crear Jerarquía de Grupos
from tai_keycloak import kc, Group
# Grupo padre
engineering = Group(
name="Engineering",
attributes={
"department": ["IT"],
"budget": ["500000"]
},
realmRoles=["developer"]
)
result = kc.admin.group.create(engineering)
# Subgrupos
backend = Group(
name="Backend",
attributes={"stack": ["Python", "Go"]}
)
frontend = Group(
name="Frontend",
attributes={"stack": ["React", "TypeScript"]}
)
kc.admin.group.create(backend, parent="Engineering")
kc.admin.group.create(frontend, parent="Engineering")
Gestión de Roles
Roles de Cliente
from tai_keycloak import kc, Role
# Crear roles para cliente 'api'
roles = [
Role(name="admin", description="Full access to API"),
Role(name="editor", description="Edit resources"),
Role(name="viewer", description="Read-only access")
]
for role in roles:
result = kc.admin.api_role.create(role)
if result.success:
print(f"✅ Role '{role.name}' creado")
Roles de Realm
# Crear role de realm
role = Role(
name="premium-user",
description="Premium subscription user"
)
result = kc.admin.realm_role.create(role)
Gestión de Clientes
Listar Clientes
result = kc.admin.client.list()
if result.success:
for client in result.data:
print(f"Cliente: {client.clientId}")
print(f" Enabled: {client.enabled}")
print(f" Protocol: {client.protocol}")
Obtener Detalles de Cliente
result = kc.admin.client.get("api")
if result.success:
client = result.data
print(f"Client ID: {client.clientId}")
print(f"Secret: {client.secret}")
print(f"Redirect URIs: {client.redirectUris}")
Tokens y Autenticación
Validar Token JWT
from tai_keycloak import kc
from tai_keycloak.service.token import TokenDAO
# Token recibido del frontend
token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
# Validar token
result = TokenDAO.decode(
token=token,
key=kc.app.public_key,
expected_audience="api",
expected_issuer="https://keycloak.company.com/realms/main"
)
if result.success:
access_token = result.data
# Información del usuario
user_id = access_token.sub
username = access_token.preferred_username
email = access_token.email
# Verificar permisos
realm_roles = access_token.realm_access.roles
api_roles = access_token.resource_access.get("api", {}).get("roles", [])
if "admin" in api_roles:
print("✅ Usuario es admin del API")
else:
print("❌ Usuario no tiene permisos de admin")
else:
print(f"❌ Token inválido: {result.message}")
🚀 Deployment
Desarrollo Local
# 1. Inicializar proyecto
tai-kc init --mode development
# 2. Levantar Keycloak
cd keycloak/
tai-kc run
# 3. Acceder
# URL: http://localhost:8090
# Admin: http://localhost:8090/admin
# Credenciales: admin / admin
Azure Web Apps
Ver guía completa en docker/workflows/GITHUB_SECRETS.md.
# 1. Inicializar proyecto
tai-kc init --mode azure
# 2. Configurar secretos en GitHub
# Ver keycloak/.github/workflows/GITHUB_SECRETS.md
# 3. Editar keycloak/.env con valores de Azure
# 4. Commit y push
git add keycloak/
git commit -m "Add Keycloak deployment"
git push origin main
# 5. El workflow se ejecuta automáticamente
On-Premise
# 1. Inicializar proyecto
tai-kc init --mode onpremise
# 2. Colocar certificados SSL
cp cert.pem keycloak/traefik/certs/
cp key.pem keycloak/traefik/certs/
# 3. Editar keycloak/.env
# 4. Levantar con Traefik
cd keycloak/
tai-kc run --profile onpremise
# 5. Acceder
# URL: https://your-domain.com/keycloak
🎨 Arquitectura y Patrones de Diseño
Patrones Implementados
1. Singleton Pattern
KeycloakAdminClient y KeycloakAppClient son singletons para reutilizar la conexión autenticada:
from tai_keycloak import kc
# Todas estas referencias apuntan a la misma instancia
admin1 = kc.admin
admin2 = kc.admin
assert admin1 is admin2 # True
2. Lazy Initialization
Los DAOs se inicializan solo cuando se acceden por primera vez:
# No se conecta hasta que se usa
admin = kc.admin
# Aquí se conecta y autentica
users = admin.user.list()
3. Result Pattern
Todas las operaciones devuelven OperationResult para manejo consistente:
result = kc.admin.user.create(user)
# Patrón consistente en toda la API
if result.success:
data = result.data
else:
error = result.error
4. DAO Pattern
Cada entidad tiene su propio Data Access Object:
UserDAO # CRUD de usuarios
GroupDAO # CRUD de grupos
ClientDAO # CRUD de clientes
RoleDAO # CRUD de roles
5. DTO Pattern
Data Transfer Objects con Pydantic para validación:
user = User(
username="john",
email="invalid-email" # ❌ ValidationError
)
Flujo de Datos
┌─────────────────────┐
│ Tu Aplicación │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ KeycloakAdminClient │
│ (Singleton) │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ UserDAO/GroupDAO │
│ (Lazy Init) │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ python-keycloak │
│ (SDK oficial) │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Keycloak REST API │
└─────────────────────┘
🎯 Ejemplos Completos
Ejemplo 1: Onboarding de Usuario
from tai_keycloak import kc, User
def onboard_employee(employee_data):
"""Onboarding completo de un empleado nuevo."""
# 1. Crear usuario
user = User(
username=employee_data["username"],
email=employee_data["email"],
firstName=employee_data["first_name"],
lastName=employee_data["last_name"],
password=employee_data["temp_password"],
attributes={
"employee_id": employee_data["employee_id"],
"department": employee_data["department"],
"hire_date": employee_data["hire_date"]
}
)
result = kc.admin.user.create(user)
if not result.success:
return result
username = result.data.username
# 2. Asignar a grupo del departamento
kc.admin.user.add_to_group(username, employee_data["department"])
# 3. Asignar roles base
kc.admin.user.assign_realm_role(username, "employee")
kc.admin.user.assign_client_role(username, "app", "viewer")
# 4. Roles específicos según posición
if employee_data["position"] == "manager":
kc.admin.user.assign_client_role(username, "api", "manager")
elif employee_data["position"] == "admin":
kc.admin.user.assign_client_role(username, "api", "admin")
return result
# Uso
employee = {
"username": "john.doe",
"email": "john@company.com",
"first_name": "John",
"last_name": "Doe",
"temp_password": "Welcome123!",
"employee_id": "EMP-2025-001",
"department": "Engineering",
"hire_date": "2025-01-15",
"position": "developer"
}
result = onboard_employee(employee)
Ejemplo 2: Middleware de Autenticación
from fastapi import FastAPI, HTTPException, Depends
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from tai_keycloak import kc
from tai_keycloak.service.token import TokenDAO
app = FastAPI()
security = HTTPBearer()
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
"""Middleware para verificar tokens JWT."""
token = credentials.credentials
result = TokenDAO.decode(
token=token,
key=kc.app.public_key,
expected_audience="api",
expected_issuer="https://keycloak.company.com/realms/main"
)
if not result.success:
raise HTTPException(status_code=401, detail="Invalid token")
return result.data
def require_role(role: str):
"""Decorator para requerir role específico."""
def decorator(access_token = Depends(verify_token)):
api_roles = access_token.resource_access.get("api", {}).get("roles", [])
if role not in api_roles:
raise HTTPException(status_code=403, detail=f"Role '{role}' required")
return access_token
return decorator
@app.get("/api/protected")
def protected_endpoint(token = Depends(verify_token)):
return {
"user": token.preferred_username,
"roles": token.resource_access.get("api", {}).get("roles", [])
}
@app.post("/api/admin")
def admin_endpoint(token = Depends(require_role("admin"))):
return {"message": "Admin access granted"}
Ejemplo 3: Sincronización de Usuarios
from tai_keycloak import kc, User
def sync_users_from_external_system(external_users):
"""Sincronizar usuarios desde sistema externo."""
# Obtener usuarios actuales de Keycloak
result = kc.admin.user.list(limit=1000)
if not result.success:
print(f"Error obteniendo usuarios: {result.message}")
return
kc_users = {u.username: u for u in result.data}
for ext_user in external_users:
username = ext_user["username"]
if username in kc_users:
# Usuario existe - actualizar
update = User(
email=ext_user["email"],
firstName=ext_user["first_name"],
lastName=ext_user["last_name"],
attributes={
"department": ext_user["department"],
"sync_date": "2025-11-25"
}
)
result = kc.admin.user.update(username, update)
status = "actualizado" if result.success else f"error: {result.message}"
else:
# Usuario nuevo - crear
user = User(
username=username,
email=ext_user["email"],
firstName=ext_user["first_name"],
lastName=ext_user["last_name"],
password=generate_temp_password(),
attributes={
"department": ext_user["department"],
"sync_date": "2025-11-25"
}
)
result = kc.admin.user.create(user)
status = "creado" if result.success else f"error: {result.message}"
print(f"Usuario {username}: {status}")
def generate_temp_password():
import secrets
return secrets.token_urlsafe(16)
🐛 Troubleshooting
Error: "Connection refused"
Causa: Keycloak no está corriendo o hostname/port incorrecto
Solución:
# Verificar que Keycloak esté corriendo
docker ps | grep keycloak
# Verificar variables de entorno
echo $MAIN_KEYCLOAK_URL
# Levantar Keycloak
tai-kc run
Error: "Invalid credentials"
Causa: Username/password incorrectos
Solución:
# Verificar credenciales en código
config = KeycloakConfig(
hostname="localhost",
port=8090,
username="admin",
password="admin" # Password por defecto
)
Error: "Token expired"
Causa: Token JWT expirado
Solución:
# Validar expiración antes de usar
result = TokenDAO.decode(token, key=public_key)
if not result.success and "expired" in result.message:
# Solicitar nuevo token al usuario
return {"error": "Token expired, please login again"}
Error: "User not found"
Causa: Usuario no existe en Keycloak
Solución:
# Verificar existencia antes de operar
result = kc.admin.user.get(username)
if not result.success:
print(f"Usuario no existe: {username}")
# Crear usuario o manejar error
Performance: Operaciones lentas
Causa: Demasiadas llamadas al API
Solución:
# ❌ Evitar: Múltiples get() en loop
for username in usernames:
user = kc.admin.user.get(username).data # N llamadas
# ✅ Mejor: Un solo list() con filtros
users = kc.admin.user.list(limit=1000).data
user_map = {u.username: u for u in users}
📚 Referencias
- Keycloak Documentation: https://www.keycloak.org/documentation
- python-keycloak SDK: https://python-keycloak.readthedocs.io/
- Pydantic: https://docs.pydantic.dev/
- Docker: https://docs.docker.com/
- Traefik: https://doc.traefik.io/traefik/
🤝 Contribuir
Para contribuir al proyecto:
- Fork el repositorio
- Crear branch feature (
git checkout -b feature/amazing-feature) - Commit cambios (
git commit -m 'Add amazing feature') - Push al branch (
git push origin feature/amazing-feature) - Abrir Pull Request
📄 Licencia
Este proyecto está bajo la licencia MIT. Ver archivo LICENSE para más detalles.
✨ Autores
- MateoSaezMata - Desarrollo inicial - triplealpha-innovation
Versión: 0.1.20
Última actualización: Noviembre 2025
Keycloak Version: 26.4.5
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tai_keycloak-0.2.1.tar.gz.
File metadata
- Download URL: tai_keycloak-0.2.1.tar.gz
- Upload date:
- Size: 69.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.2.1 CPython/3.11.0 Linux/6.11.0-1018-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
63d1cffd2b6672b9798d58e0e620d26266d7596c0cf4a68eaee808564afb0b30
|
|
| MD5 |
f665f9b16efc311e35a7bb80cd78720d
|
|
| BLAKE2b-256 |
29426576b3b7abde209ff76a5119f7ac02113ccd961e21a7d0b9e5bcc2ad149c
|
File details
Details for the file tai_keycloak-0.2.1-py3-none-any.whl.
File metadata
- Download URL: tai_keycloak-0.2.1-py3-none-any.whl
- Upload date:
- Size: 90.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.2.1 CPython/3.11.0 Linux/6.11.0-1018-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a5390f2504882e49b55e8810e4ff4873ff816d5d6493316796185f62bca29e4e
|
|
| MD5 |
4a00fc3c2630b438030a69c6da11e700
|
|
| BLAKE2b-256 |
ae0acf8218ae34e30eba598c66ac00d6a923197ab7d96eb2974ec5206b61bcac
|