Skip to main content

Libreria de autenticacion OIDC con Keycloak para FastAPI, Flask y Django

Project description

Auth Guardian

PyPI version Python versions License

Librería para integrar FastAPI, Flask y Django con Keycloak sin complicarte con OIDC desde cero.


¿Para qué sirve?

Con auth-guardian tienes listo:

  • Login OIDC con Keycloak.
  • Rutas /login, /oidc/callback y /logout.
  • Protección de endpoints autenticados.
  • Protección por rol (admin, etc.).
  • Validación en tiempo real por introspection.
  • Logout con revocación inmediata del refresh token.

Instalación

Usa el comando según tu framework:

# FastAPI (por defecto)
pip install auth-guardian
# Flask
pip install "auth-guardian[flask]"
# Django
pip install "auth-guardian[django]"

Requisitos: Python >= 3.10


Configuración rápida

1. Variables de entorno obligatorias

La librería falla al iniciar con un mensaje claro si falta alguna de estas variables de entorno:

KEYCLOAK_BASE_URL=
KEYCLOAK_REALM=
KEYCLOAK_CLIENT_ID=
KEYCLOAK_CLIENT_SECRET=

KEYCLOAK_CLIENT_SECRET se usa tanto para introspection como para firmar/verificar el state anti-CSRF.

2. Configuración en Keycloak

Antes de arrancar, verifica que tu cliente en Keycloak esté configurado correctamente:

  1. Crear cliente de tipo OIDC.
  2. Activar Client authentication (cliente confidencial).
  3. Configurar el Client Secret.
  4. Agregar la Redirect URI de tu app para el callback OIDC.
  5. Asignar roles en realm o client según tu modelo de autorización.

Integración con FastAPI

Crea un archivo auth.py y pega esto:

from __future__ import annotations

import os
from typing import Any

from fastapi import Depends, FastAPI, HTTPException, status
from pydantic import BaseModel, Field

from auth_guardian import AuthGuardian, KeycloakAPIError, create_auth_router

KEYCLOAK_ADMIN_CLIENT_ID = os.getenv("KEYCLOAK_ADMIN_CLIENT_ID", "")
KEYCLOAK_ADMIN_CLIENT_SECRET = os.getenv("KEYCLOAK_ADMIN_CLIENT_SECRET", "")

app = FastAPI(title="AuthGuardian Demo")
auth = AuthGuardian()

app.include_router(
    create_auth_router(
        auth=auth,
        login_redirect_url="/profile",
        logout_redirect_url="/login",
    )
)


class RegisterRequest(BaseModel):
    username: str = Field(min_length=3, max_length=64)
    email: str = Field(min_length=5, max_length=128)
    password: str = Field(min_length=8, max_length=128)
    first_name: str = ""
    last_name: str = ""


@app.get("/health")
async def health() -> dict[str, str]:
    return {"status": "ok"}


@app.get("/profile")
async def profile(user: dict[str, Any] = Depends(auth.get_current_user)) -> dict[str, Any]:
    return {
        "mensaje": f"Hola {user.get('preferred_username')}",
        "email": user.get("email"),
        "sub": user.get("sub"),
    }


@app.get("/admin")
async def admin(user: dict[str, Any] = Depends(auth.require_role("admin"))) -> dict[str, str]:
    return {"mensaje": "Acceso permitido para rol admin"}


@app.post("/register", status_code=status.HTTP_201_CREATED)
async def register(payload: RegisterRequest) -> dict[str, Any]:
    try:
        user = await auth.oidc_client.create_user_via_admin_api(
            admin_client_id=KEYCLOAK_ADMIN_CLIENT_ID,
            admin_client_secret=KEYCLOAK_ADMIN_CLIENT_SECRET,
            username=payload.username,
            email=payload.email,
            first_name=payload.first_name,
            last_name=payload.last_name,
            password=payload.password,
            enabled=True,
            email_verified=False,
        )
    except KeycloakAPIError as exc:
        raise HTTPException(status_code=exc.status_code, detail=exc.detail) from exc

    return {"mensaje": "Usuario creado correctamente", "usuario": user}

Integración con Flask (roles y auth por decorador)

from flask import Flask, jsonify, g

from auth_guardian import AuthGuardian
from auth_guardian.flask import FlaskAuthIntegration

app = Flask(__name__)
auth = AuthGuardian()
flask_auth = FlaskAuthIntegration(auth)

flask_auth.register_auth_routes(
    app,
    login_redirect_url="/profile",
    logout_redirect_url="/login",
)


@app.get("/profile")
@flask_auth.require_auth()
def profile():
    return jsonify(g.auth_user)


@app.get("/admin")
@flask_auth.require_role("admin")
def admin():
    return jsonify({"ok": True})

Esto registra rutas OIDC completas en Flask: /login, /oidc/callback, /logout.


Integración con Django (roles y auth por decorador)

from django.http import JsonResponse
from django.urls import path

from auth_guardian import AuthGuardian
from auth_guardian.django import DjangoAuthIntegration

auth = AuthGuardian()
django_auth = DjangoAuthIntegration(auth)


@django_auth.require_auth()
def profile(request):
    return JsonResponse(request.auth_user)


@django_auth.require_role("admin")
def admin(request):
    return JsonResponse({"ok": True})


urlpatterns = [
    *django_auth.build_auth_urlpatterns(
        login_redirect_url="/profile/",
        logout_redirect_url="/login/",
    ),
    path("profile/", profile),
    path("admin/", admin),
]

Esto registra rutas OIDC completas en Django: /login/, /oidc/callback/, /logout/.


Flujos de autenticación

1. Login OIDC

El usuario accede a /login y es redirigido al servidor Keycloak. Una vez que ingresa sus credenciales, Keycloak devuelve un code al callback, que la librería intercambia por los tokens.

Flujo de Login OIDC


2. Request protegido (introspection)

En cada request a un endpoint protegido, el token del cliente es validado en tiempo real contra Keycloak. No hay caché ni validación local por defecto.

Flujo de Introspection


3. Logout

El logout revoca el refresh token en Keycloak antes de borrar las cookies, garantizando que la sesión quede inválida de inmediato en el servidor.

Flujo de Logout


Validación de token

AuthGuardian valida el token en cada request consultando a Keycloak en tiempo real mediante /token/introspect.

Comportamiento ante errores:

Situación Respuesta
active: false 401 Unauthorized
Keycloak no disponible 503 Service Unavailable (sin exponer detalles internos)
API de Keycloak caída Falla introspection aunque la BD de Keycloak esté activa

API pública estable

Uso habitual:

  • AuthGuardian
  • create_auth_router
  • FlaskAuthIntegration.register_auth_routes
  • DjangoAuthIntegration.build_auth_urlpatterns
  • auth.get_current_user
  • auth.require_role

Troubleshooting

Missing required configuration variables

Causa: Falta una o más variables de entorno obligatorias.

Solución: Revisa y completa la sección Configuración rápida > Variables de entorno obligatorias.


ModuleNotFoundError: No module named 'fastapi'

Causa: El entorno quedó sin dependencias instaladas (por ejemplo, instalación con --no-deps o entorno dañado) y luego se importó AuthGuardian o create_auth_router.

Solución:

  • Reinstala con pip install auth-guardian.
  • En tus dependencias del proyecto, usa auth-guardian>=0.1.31.

ModuleNotFoundError: No module named 'flask' o No module named 'django'

Causa: Se intentó usar el adaptador de Flask/Django sin instalar ese extra.

Solución:

  • Flask: pip install "auth-guardian[flask]"
  • Django: pip install "auth-guardian[django]"
  • FastAPI: pip install auth-guardian (no requiere extra)

Introspection devuelve 401 o 403

Causa: El cliente no está configurado como confidencial en Keycloak, o el secret es incorrecto.

Solución:

  • Verificar KEYCLOAK_CLIENT_SECRET en tu .env.
  • Confirmar que el cliente tiene Client authentication activado en Keycloak.

Login falla en callback (Redirect URI mismatch)

Causa: La Redirect URI configurada en Keycloak no coincide con la que usa la app.

Solución:

  • Revisar la Valid Redirect URIs del cliente en Keycloak.
  • Si estás detrás de un proxy inverso, verificar que se pasen correctamente las cabeceras Host y X-Forwarded-*.

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

auth_guardian-0.1.34.tar.gz (32.6 kB view details)

Uploaded Source

Built Distribution

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

auth_guardian-0.1.34-py3-none-any.whl (31.8 kB view details)

Uploaded Python 3

File details

Details for the file auth_guardian-0.1.34.tar.gz.

File metadata

  • Download URL: auth_guardian-0.1.34.tar.gz
  • Upload date:
  • Size: 32.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for auth_guardian-0.1.34.tar.gz
Algorithm Hash digest
SHA256 0af634bd9c1c44de9b004ec65ae7e4a94b1ffd97000036e5016760a93141f85f
MD5 21d9d55962b61480e02026d56eae6be0
BLAKE2b-256 43b9eda44acd87422fd7be36e9a9ea544bcec22ed9fee63a48888f292fcaae76

See more details on using hashes here.

File details

Details for the file auth_guardian-0.1.34-py3-none-any.whl.

File metadata

  • Download URL: auth_guardian-0.1.34-py3-none-any.whl
  • Upload date:
  • Size: 31.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for auth_guardian-0.1.34-py3-none-any.whl
Algorithm Hash digest
SHA256 a7c3fcc0c9bb68c18fbd8a9f9bd518cd7e0870ff1f9d87b93e249e0a3d2cb9d8
MD5 c1d235c995eae5d3dfb424b77267b794
BLAKE2b-256 75808cb9d613acbbdef305df68841a2dca11ed242dbc7d5c58193d15837a92ad

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