Metadata-Version: 2.4
Name: auth-guardian
Version: 0.1.19
Summary: Libreria de autenticacion OIDC con Keycloak para FastAPI
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

<p align="center">
  <img src="https://img.shields.io/pypi/v/auth-guardian?color=blue&label=PyPI" alt="PyPI version"/>
  <img src="https://img.shields.io/pypi/pyversions/auth-guardian" alt="Python versions"/>
  <img src="https://img.shields.io/pypi/l/auth-guardian" alt="License"/>
</p>

> Librería Python para integrar autenticación OIDC con Keycloak en FastAPI — con foco en seguridad, revocación inmediata y configuración mínima.

---

## ¿Qué resuelve?

Sin `auth-guardian` tendrías que implementar a mano el flujo OIDC completo, la validación de tokens, el manejo de cookies y la revocación en logout. Con esta librería, todo eso queda resuelto en unas pocas líneas:

| Problema | Solución |
|---|---|
| Flujo OIDC completo | Rutas `/login`, `/oidc/callback`, `/logout` listas para usar |
| Protección de endpoints | `Depends(auth.get_current_user)` |
| Control de acceso por rol | `Depends(auth.require_role("admin"))` |
| Validación de token en cada request | Introspection contra Keycloak (activo por defecto) |
| Revocación inmediata en logout | Revoke del refresh token + invalidación por introspection |

---

## Instalación

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

**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:

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

> `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

### Setup inicial

```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",   # A dónde redirige después del login exitoso
        logout_redirect_url="/login",      # A dónde redirige después del logout
    )
)
```

Esto registra automáticamente tres rutas en tu app:

| Ruta | Método | Qué hace |
|---|---|---|
| `/login` | GET | Redirige al usuario a la pantalla de login de Keycloak |
| `/oidc/callback` | GET | Recibe el código de Keycloak, lo intercambia por tokens y guarda las cookies de sesión |
| `/logout` | GET | Revoca el token en Keycloak, borra las cookies y redirige |

No necesitas implementar ninguna de estas rutas manualmente.

---

### GET /login

El usuario navega a esta ruta (o le pones un botón que apunte a ella). La librería construye la URL de autorización y redirige a Keycloak automáticamente.

```python
# No necesitas definir esta ruta — ya existe gracias a create_auth_router.
# Úsala desde el frontend así:
# <a href="/login">Iniciar sesión</a>
```

---

### GET /oidc/callback

Keycloak redirige aquí después de que el usuario ingresa sus credenciales. La librería intercambia el `code` por los tokens y guarda la sesión en cookies. El usuario no interactúa con esta ruta directamente.

```python
# Tampoco defines esta ruta manualmente.
# Solo asegúrate de que la Redirect URI en Keycloak apunte a:
# https://tu-dominio.com/oidc/callback
```

---

### GET /logout

Revoca el refresh token en Keycloak y limpia las cookies de sesión. Apunta un botón a esta ruta para cerrar la sesión del usuario.

```python
# <a href="/logout">Cerrar sesión</a>
```

---

### Proteger tus propios endpoints

Una vez que el usuario tiene sesión activa, puedes proteger cualquier endpoint de dos formas:

**Por autenticación** — solo requiere que el usuario haya hecho login:

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

**Por rol** — además del login, verifica que el usuario tenga el rol indicado:

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

Si el usuario no está autenticado recibe un `401`. Si está autenticado pero no tiene el rol, recibe un `403`.

---

## 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](https://mermaid.ink/img/c2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBVIGFzIFVzdWFyaW8KICAgIHBhcnRpY2lwYW50IEEgYXMgQVBJIEZhc3RBUEkKICAgIHBhcnRpY2lwYW50IEsgYXMgS2V5Y2xvYWsKCiAgICBVLT4-QTogR0VUIC9sb2dpbgogICAgQS0-Pks6IFJlZGlyZWN0IEF1dGhvcml6YXRpb24gUmVxdWVzdAogICAgSy0tPj5VOiBMb2dpbiBVSQogICAgVS0-Pks6IENyZWRlbmNpYWxlcwogICAgSy0tPj5BOiBSZWRpcmVjdCAvb2lkYy9jYWxsYmFjaz9jb2RlPS4uLgogICAgQS0-Pks6IEV4Y2hhbmdlIGNvZGUgcG9yIHRva2VucwogICAgSy0tPj5BOiBhY2Nlc3NfdG9rZW4gKyByZWZyZXNoX3Rva2VuCiAgICBBLS0-PlU6IFNldCBjb29raWVzICsgcmVkaXJlY3QgYXBw)

---

### 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](https://mermaid.ink/img/c2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBDIGFzIENsaWVudGUKICAgIHBhcnRpY2lwYW50IEEgYXMgQVBJCiAgICBwYXJ0aWNpcGFudCBLIGFzIEtleWNsb2FrCgogICAgQy0-PkE6IFJlcXVlc3QgY29uIHRva2VuCiAgICBBLT4-SzogUE9TVCAvdG9rZW4vaW50cm9zcGVjdAogICAgSy0tPj5BOiBhY3RpdmU6IHRydWUgIOKGkiAyMDAgT0sKICAgIEstLT4-QTogYWN0aXZlOiBmYWxzZSDihpIgNDAxIFVuYXV0aG9yaXplZA==)

---

### 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](https://mermaid.ink/img/c2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBVIGFzIFVzdWFyaW8KICAgIHBhcnRpY2lwYW50IEEgYXMgQVBJCiAgICBwYXJ0aWNpcGFudCBLIGFzIEtleWNsb2FrCgogICAgVS0-PkE6IEdFVCAvbG9nb3V0CiAgICBBLT4-SzogUE9TVCAvcmV2b2tlIChyZWZyZXNoX3Rva2VuKQogICAgSy0tPj5BOiAyMDAvMjA0CiAgICBBLS0-PlU6IERlbGV0ZSBjb29raWVzICsgcmVkaXJlY3Q=)

---

## Validación de token

`AuthGuardian` usa **introspection** por defecto — cada request verifica el token contra Keycloak en tiempo real.

| Modo | Comportamiento |
|---|---|
| `introspection` (default) | Llama a `/token/introspect` en cada request |

**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

Todo lo que necesitas está disponible directamente desde `auth_guardian`. Aquí un resumen de qué hace cada cosa y cuándo la usarías:

```python
from auth_guardian import (
    AuthGuardian,           # Lo primero que instancias. Da acceso a get_current_user y require_role.
    create_auth_router,     # Crea las rutas /login, /oidc/callback y /logout para registrar en tu app.
    extract_client_roles,   # Función de utilidad: extrae los roles de un cliente a partir del token decodificado.
    AuthConfig,             # Si necesitas personalizar la configuración más allá de las variables de entorno.
    AuthTokenValidator,     # Validador de tokens. Úsalo si quieres extender o reemplazar la lógica de validación.
    AuthOIDCClient,         # Cliente que habla con Keycloak. Útil si necesitas hacer llamadas OIDC directamente.
    KeycloakAuthError,      # Excepción: el usuario no está autenticado o su sesión no es válida.
    TokenValidationError,   # Excepción: el token existe pero no pasa la validación.
    KeycloakAPIError,       # Excepción: no se pudo comunicar con Keycloak (timeout, caída, etc.).
)
```

Para el uso habitual solo necesitas `AuthGuardian` y `create_auth_router`. El resto está disponible si quieres extender el comportamiento o manejar errores de forma personalizada.

---

## Troubleshooting

### `Missing required configuration variables`

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

**Solución:** Completar las cuatro variables en tu `.env`:
```env
KEYCLOAK_BASE_URL=
KEYCLOAK_REALM=
KEYCLOAK_CLIENT_ID=
KEYCLOAK_CLIENT_SECRET=
```

---

### 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-*`.
