Metadata-Version: 2.4
Name: auth-guardian
Version: 0.1.9
Summary: Reusable Keycloak SDK for auth-service and microservices
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: httpx<0.29,>=0.28
Requires-Dist: python-jose[cryptography]<4,>=3.5
Provides-Extra: database
Requires-Dist: sqlalchemy<3,>=2.0; extra == "database"
Requires-Dist: aiosqlite<1,>=0.20; extra == "database"
Provides-Extra: fastapi
Requires-Dist: fastapi<0.119,>=0.118; extra == "fastapi"
Provides-Extra: dev
Requires-Dist: build<2,>=1.3; extra == "dev"
Requires-Dist: fastapi<0.119,>=0.118; extra == "dev"
Requires-Dist: mypy<2,>=1.10; extra == "dev"
Requires-Dist: pytest<9,>=8.3; extra == "dev"
Requires-Dist: pytest-asyncio<2,>=1.2; extra == "dev"
Requires-Dist: respx<0.23,>=0.22; extra == "dev"
Requires-Dist: ruff<0.15,>=0.14; extra == "dev"
Requires-Dist: twine<7,>=6.2; extra == "dev"

# Auth Guardian

`auth-guardian` es una libreria Python para proteger APIs con Keycloak/OIDC en FastAPI, con configuracion minima y enfoque plug-and-play.

## Que resuelve

- Login OIDC sin escribir todo el flujo a mano.
- Proteccion de endpoints autenticados.
- Control por roles (`admin`, `support`, etc.).
- Helpers para validacion de tokens y extraccion de roles.

## Instalacion

```bash
pip install auth-guardian
```

Documentacion oficial y paquete:
- https://pypi.org/project/auth-guardian/

## Configuracion minima

Define estas variables de entorno:

```env
KEYCLOAK_BASE_URL=https://auth.tu-dominio.com
KEYCLOAK_REALM=tu-realm
KEYCLOAK_CLIENT_ID=tu-cliente
KEYCLOAK_CLIENT_SECRET=tu-secret
```

Tambien se aceptan alias legacy:

- `AUTH_BASE_URL`
- `AUTH_REALM`
- `AUTH_CLIENT_ID`

## Revocación de tokens

`AuthGuardian` esta orientado a revocacion inmediata con Keycloak:

- Cada request valida el token por introspection contra Keycloak.
- Si Keycloak responde `active=false`, la libreria rechaza con `401`.
- En logout, la libreria revoca el `refresh_token` en Keycloak y borra cookies.

## Modo de validación de token

`AuthGuardian` usa validación por introspection:

| Modo | Comportamiento |
|---|---|
| `introspection` (default) | Valida cada request contra Keycloak (`/token/introspect`) |

Uso por código (introspection ya viene por defecto):

```python
from auth_guardian import AuthGuardian

auth = AuthGuardian()
```

Notas:
- Si `active=false` en introspection, se rechaza con `401`.
- Si Keycloak no está disponible, responde `503` sin exponer detalles internos.
- Si cae la API de Keycloak, introspection falla aunque la BD de Keycloak siga arriba.
- `KEYCLOAK_CLIENT_SECRET` se usa para introspection y, por defecto, para firmar/verificar `state` anti-CSRF.

## Variables obligatorias

Si falta una variable obligatoria, `AuthGuardian` registra un log de error y falla al iniciar con mensaje claro.

Variables requeridas:
- `KEYCLOAK_BASE_URL`
- `KEYCLOAK_REALM`
- `KEYCLOAK_CLIENT_ID`
- `KEYCLOAK_CLIENT_SECRET`

## Uso rapido en FastAPI

```python
from fastapi import Depends, FastAPI
from auth_guardian import AuthGuardian, create_auth_router

app = FastAPI()
auth = AuthGuardian()

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

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

@app.get("/ejemplo/v1")
async def ejemplo_v1(user: dict = Depends(auth.get_current_user)):
    return {
        "mensaje": f"Hola {user.get('preferred_username')}",
        "email": user.get("email"),
    }

@app.get("/ejemplo/v2")
async def ejemplo_v2(user: dict = Depends(auth.require_role("admin"))):
    return {"mensaje": "Acceso permitido para rol admin"}
```

Con esto tienes:

- `GET /login`
- `GET /oidc/callback`
- `GET /logout`
- Proteccion por usuario autenticado y por rol.

## Casos de uso comunes

- Proteger endpoints internos de microservicios.
- Habilitar login/logout en apps FastAPI sin implementar OIDC desde cero.
- Restringir secciones administrativas por rol.
- Validar tokens manualmente en procesos asincronos o workers.

## Ejemplo de validacion manual

```python
payload = await auth.get_validator().decode_access_token(token)
print(payload.get("sub"))
```

## Caracteristicas opcionales

### Multi-tenant (multiples realms)

```python
from fastapi import Request

async def resolve_tenant(request: Request) -> str | None:
    return request.headers.get("X-Tenant-ID")

auth = AuthGuardian(tenant_resolver=resolve_tenant)
```

## API publica estable

- `AuthGuardian`
- `create_auth_router`
- `extract_client_roles(payload, client_id)`
- `AuthConfig`
- `AuthTokenValidator`
- `AuthOIDCClient`
- `KeycloakAuthError`
- `TokenValidationError`
- `KeycloakAPIError`

## Buenas practicas

- Usa variables de entorno por ambiente (dev/stage/prod).
- No hardcodees secretos en codigo fuente.
- Fija version en produccion (`auth-guardian==x.y.z`).
