SDK to consume cidi-auth and auth.back authentication services from Python apps
Project description
boolean-auth-sdk (Python) — Integración Django/DRF
SDK para que juzgado.back y otros proyectos Django/DRF consuman los servicios de autenticación cidi-auth y auth.back, que a su vez delegan la firma de tokens en tokens.back.
📱 Cliente (frontend/backend) ──▶ cidi-auth / auth.back ──▶ tokens.back (.well-known/jwks.json)
boolean_auth_sdk.AuthClient: cliente HTTP para login, refresh, logout,/user/me/,/user/by-cuil/.boolean_auth_sdk.integrations.django_drf.JWTAuthentication: autenticación DRF lista para usar, con soporte de fallback local y extracción de payload completo.boolean_auth_sdk.JWTVerifier: verificación manual via JWKS con headers tipo navegador (evita bloqueos de Cloudflare) + fallback local (PKCS#8→ extrae pública automáticamente).
Instalación
# Core + integración Django/DRF
pip install -e "./sdks/python[django]"
# Dependencias de desarrollo (pytest + responses) para correr tests del SDK
pip install -e "./sdks/python[dev]"
1. Configuración en Django (settings.py)
# URL al JWKS que expone tokens.back (mismo que usan auth.back / cidi-auth)
AUTH_SDK_JWKS_URL = "https://tokens.boolean.com.ar/.well-known/jwks.json"
# Algoritmos permitidos (por defecto RS256)
AUTH_SDK_ALGORITHMS = ["RS256"]
# (Opcional) Fallback local: PEM privado o público. Si es privado, se extrae la pública.
AUTH_SDK_LOCAL_SECRET = env.str("JWT_LOCAL_PEM", default=None)
# Prefijos aceptados en Authorization (case-insensitive)
AUTH_SDK_HEADER_PREFIXES = ["token", "bearer"]
# Si el token llega vía cookie HttpOnly
AUTH_SDK_COOKIE_NAME = "access_token" # opcional
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"boolean_auth_sdk.integrations.django_drf.JWTAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
}
JWTAuthenticationdejarequest.usercomo SDKUser, con:user_id,username,email,app_id,session_uuidroles: lista deRole(role_code, role_name)payload: claims completos del JWT
JWTRefreshAuthenticationes idéntica pero fuerzatoken_type == "refresh".
2. Views y ViewSets
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
class CasesViewSet(viewsets.ViewSet):
permission_classes = [] # usa las globales (IsAuthenticated)
def list(self, request):
user = request.user # SDKUser
return Response({
"user_id": user.user_id,
"roles": [r.role_code for r in user.roles],
"email": user.email,
"payload": user.payload,
})
@action(detail=False, methods=["post"],
authentication_classes=[JWTRefreshAuthentication])
def refresh(self, request):
# Token ya validado como refresh
return Response({"message": "refresh validado"})
- Se puede combinar con cualquier permiso (
IsAuthenticated, personalizados, etc.). JWTRefreshAuthenticationútil para endpoints que aceptan sólo refresh tokens (ej: reinicio de sesión).
3. Mapear a django.contrib.auth.User
Si tu app necesita usuarios persistentes (grupos, staff, etc.), subclasá JWTAuthentication y sobreescribí get_user para sincronizar el payload con tu modelo.
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from boolean_auth_sdk.integrations.django_drf import JWTAuthentication
ROLE_TO_GROUP = {
"judge": "Juzgado",
"prosecutor": "Procurador",
"secretary": "Secretario",
"auxiliar": "Auxiliar",
"admin": "admin",
}
class DjangoUserJWTAuthentication(JWTAuthentication):
def get_user(self, payload, token):
User = get_user_model()
user_id = payload.get("user_id") or payload.get("sub")
email = payload.get("email")
username = payload.get("username") or email or user_id
user, _ = User.objects.get_or_create(username=username)
user.email = email or user.email
user.first_name = payload.get("first_name") or user.first_name
user.last_name = payload.get("last_name") or user.last_name
user.is_active = True
user.save()
user.groups.clear()
for role in payload.get("roles", []):
code = (role.get("role_code") or role.get("code") or "").lower()
group_name = ROLE_TO_GROUP.get(code)
if group_name:
group, _ = Group.objects.get_or_create(name=group_name)
user.groups.add(group)
# Adjuntar payload completo para acceso posterior
user.payload = payload
user.roles = payload.get("roles", [])
user.token = token
return user
En settings.py reemplazá el path de la clase:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"myproject.authentication.DjangoUserJWTAuthentication",
],
}
4. Endpoint de refresh + reemisión de tokens
Ejemplo de endpoint que usa el refresh token del header y reemite llamando a AuthClient.refresh():
from rest_framework.views import APIView
from rest_framework.response import Response
from boolean_auth_sdk import AuthClient
from boolean_auth_sdk.integrations.django_drf import JWTRefreshAuthentication
class RefreshView(APIView):
authentication_classes = [JWTRefreshAuthentication]
permission_classes = [] # opcional: IsAuthenticated ya aplicada globalmente
def post(self, request):
refresh_token = request.auth # el string pasado en Authorization
client = AuthClient(settings.CIDI_AUTH_BASE_URL)
tokens = client.refresh(refresh_token)
return Response({
"access_token": tokens.access_token,
"refresh_token": tokens.refresh_token,
})
5. Consumir los endpoints de auth desde Django
Para llamar a cidi-auth o auth.back desde tus views/services (por ejemplo, buscar usuarios por CUIL):
from boolean_auth_sdk import AuthClient
client = AuthClient(settings.CIDI_AUTH_BASE_URL, app_id=settings.CIDI_APP_ID)
def buscar_usuario_por_cuil(cuil: str, token: str):
user = client.find_by_cuil(cuil, access_token=token)
return user
def info_propia(token: str):
return client.me(token)
AuthClientrespeta el timeout (TOKEN_SIGN_SERVICE_TIMEOUTen config) y envía User-Agent tipo navegador.
6. Autenticación vía cookies
Si el frontend recibe tokens como cookies HttpOnly (access_token / refresh_token), configurá:
AUTH_SDK_COOKIE_NAME = "access_token"
REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"] = [
"boolean_auth_sdk.integrations.django_drf.JWTAuthentication",
]
- Asegurate de configurar
SESSION_COOKIE_SECURE,CSRF_COOKIE_SECURE,SameSitesegún tu despliegue. JWTAuthenticationintentará primero el header Authorization y luego la cookie.
7. Migrar desde un autenticador JWT existente
Checklist típico (ej. migración de authentication/cidi_jwt_authentication.py en juzgado.back):
- Agregar
boolean_auth_sdkarequirements.txt/ instalar editable. - Configurar valores
AUTH_SDK_*ensettings.py(los mismos que usancidi-auth/auth.back). - Reemplazar
DEFAULT_AUTHENTICATION_CLASSESpor la clase del SDK (o subclase custom). - Si tu autenticador antiguo re-hidrataba usuarios de Django, copiar la lógica a una subclase de
JWTAuthentication.get_user(). - Actualizar tests para usar el nuevo flujo (o mockear
JWTVerifier).
8. Verificación manual y scripts
from boolean_auth_sdk import JWTVerifier
verifier = JWTVerifier(
jwks_url=settings.AUTH_SDK_JWKS_URL,
local_secret=settings.AUTH_SDK_LOCAL_SECRET,
)
payload = verifier.verify(token, expected_token_type="access")
print(payload["user_id"], payload["roles"])
- Si
local_secretes un PEM privado RS*, se extrae la pública automáticamente. TokenExpiredError/InvalidTokenErrorayudan a distinguir expirados de inválidos.
9. Manejo de errores
| Excepción | Cuándo ocurre |
|---|---|
AuthenticationError |
auth back devuelve 401/403 (credenciales inválidas) |
HTTPError |
otro status HTTP (500, 404, etc.) |
InvalidTokenError |
firma inválida / JWKS no coincide |
TokenExpiredError |
el JWT está expirado |
Consejos:
- Revisa que
AUTH_SDK_JWKS_URLapunte al.well-known/jwks.jsondetokens.back(no al de cidi-auth). - Si Cloudflare bloquea el JWKS, verifica los headers (el SDK usa un User-Agent tipo Chrome).
- Usá
local_secretcomo fallback sólo mientras resuelves problemas de networking.
10. Referencia de settings
| Setting | Default | Descripción |
|---|---|---|
AUTH_SDK_JWKS_URL |
None (requerido) |
URL al JWKS (https://…/.well-known/jwks.json) |
AUTH_SDK_ALGORITHMS |
["RS256"] |
Algoritmos aceptados |
AUTH_SDK_LOCAL_SECRET |
None |
PEM local (fallback) |
AUTH_SDK_AUDIENCE |
None |
Audiencia esperada (si querés validar aud) |
AUTH_SDK_HEADER_PREFIXES |
["token", "bearer"] |
Prefijos aceptados en Authorization |
AUTH_SDK_COOKIE_NAME |
None |
Nombre de cookie con el access token |
AUTH_SDK_REQUIRED_TOKEN_TYPE |
None |
Forzado en subclases (ej. refresh) |
11. Tests del SDK
cd sdks/python
pip install -e ".[dev]"
pytest
Usa responses + claves RSA realistas para validar JWKS, fallback local y errores.
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 boolean_auth_sdk-0.1.0.tar.gz.
File metadata
- Download URL: boolean_auth_sdk-0.1.0.tar.gz
- Upload date:
- Size: 15.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
85468c4d2d5d07e9f507f7dd49f9f2b79b7d1399f6e350226975fc79cf563f81
|
|
| MD5 |
98723f761ba3b9c530d973112df8962c
|
|
| BLAKE2b-256 |
214fe2af1bf469fda24433769585c6972dbf744127ed386f435a17406ca1160c
|
File details
Details for the file boolean_auth_sdk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: boolean_auth_sdk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
22b6a1e6b5940f2b7d847b86a9911f927ebe8afebfee63078f8bd1753c3a7ffb
|
|
| MD5 |
ccd2c23f4f3e53ef38156338c26ded22
|
|
| BLAKE2b-256 |
38c07384e7fa302e8810ded9a14eaad26d362bf71fcd9031f94d2408ff9117f2
|