Skip to main content

Datagrowth common: Supabase Auth (cookie JWT + Admin API), API helpers, structured logging, result pattern, Supabase client, shared models and Jinja2 + Tailwind UI subpackage.

Project description

datagrowth-common

Repo de doble propósito:

  1. Paquete Python (src/datagrowth_common/): auth (Supabase + cookie HMAC), UI (Jinja2 + CSS + JS), logging, helpers de API. Se instala vía pip.
  2. GitHub template (template/): scaffold FastAPI listo para Datagrowth. Se instancia con gh repo create --template datagrowth/datagrowth-common.

Utilidades compartidas entre las apps del ecosistema Datagrowth (app-backend, ai-automation, fable, chatbot y las apps de cliente generadas por el pipeline multi-agente).

Quickstart como template (nuevo repo de app)

gh repo create my-new-app --template datagrowth/datagrowth-common --private
gh repo clone datagrowth/my-new-app
cd my-new-app
cp infra/.env.example .env  # rellena las claves Supabase
docker compose -f infra/docker-compose.yml up -d --wait
curl http://localhost:8000/healthz

Tras esto, abre Cursor / Claude Code en el repo y pega el prompt de onboarding del README.md del propio repo recién creado.

Quickstart como dependencia pip

pip install "datagrowth-common[supabase]~=0.3.1"

A partir de v0.3.0, el único modelo de autenticación es Supabase Auth (cookie httpOnly con JWT). Los helpers contra Authentik (forward-auth y OIDC propio) se eliminaron — ver CHANGELOG.md para la guía de migración. La cookie HMAC propia se conserva en auth.session_hmac como fallback para apps internas que no usan Supabase Auth.

Qué incluye

Supabase Auth (v0.3.0+)

Módulo Contenido
auth.supabase verify_supabase_jwt(token) — valida HS256 + exp + aud. SupabaseUser (modelo Pydantic con id, email, role). require_supabase_jwt (FastAPI dep que lee Authorization: Bearer). SupabaseUserDep (alias tipado). require_role(role) (factory de dep con check de app_metadata.role).
auth.session Cookie httpOnly server-rendered: set_session(response, access_token, refresh_token), clear_session, get_session_tokens, require_session (lee cookie + valida JWT), SessionUserDep, refresh_session_tokens (canjea refresh → nuevos tokens). Pensado para apps Jinja+HTMX donde el cliente NO maneja JWTs en JS.
auth.router make_auth_router(post_login_redirect, post_logout_redirect) y auth_router por defecto. Endpoints reutilizables: POST /auth/password, POST /auth/register, POST /auth/magic-link/request, POST /auth/password/reset, GET /auth/oauth/{provider}/start, GET /auth/oauth/{provider}/callback, POST /auth/refresh, POST /logout, GET /api/me. La página /login HTML la sirve cada app con su template propio (branding).
auth.supabase_admin Wrappers async sobre Supabase Admin API con patrón Result: list_users, invite_user_by_email, delete_user, update_user_role, create_user_with_password, send_password_reset.
auth.session_hmac Cookie de sesión firmada con HMAC propio (DG_SESSION_SECRET). Fallback para apps internas que no usan Supabase Auth. Antes de v0.3.0 se llamaba auth.local.
api.users users_router — APIRouter con GET /api/users (paginado, admin), POST /api/users/invite (admin), DELETE /api/users/{id} (admin), GET /api/users/me.
supabase get_supabase_client(schema) (anon key) y get_supabase_admin_client(schema) (service_role key).

Genérico

Módulo Contenido
result Result[T], ok(), err() — patrón de error como valor
logger setup_logging(), get_logger() — structlog preconfigurado (JSON en prod, consola en dev)
api.errors ApiResponse, ErrorBody, ErrorCode, ok_response, err_response, HTTP_STATUS_FOR_CODE
api.pagination Paginated[T], PageMeta, paginate_params, build_meta
shared_models ClientRead, ProjectRead, ContactRead — modelos Pydantic de lectura
updater.checker check_for_updates(), run_periodic_check() — verificador de versiones async
testing.fixtures signed_session_cookie, mock_supabase_client y otros fixtures pytest

Eliminado en v0.3.0 (breaking)

Módulo Reemplazo
authentik auth.supabase (validación JWT)
auth_fastapi auth.session.require_session (cookie JWT) o auth.supabase.require_supabase_jwt (Bearer)
auth.local auth.session_hmac (rename). Las funciones OIDC se eliminan; usa auth.router.make_auth_router() para login + OAuth + magic link + reset via Supabase

auth.local se conserva como alias deprecated (DeprecationWarning) hasta v0.4.0. Ver CHANGELOG.md para la guía de migración completa.

Instalación

Publicado en PyPI: https://pypi.org/project/datagrowth-common/.

pip install "datagrowth-common[supabase]>=0.3.2,<0.4"

El extra supabase añade supabase>=2.0 y pyjwt>=2.9 (necesarios para verify_supabase_jwt y users_router).

En pyproject.toml de otra app

[project]
dependencies = [
    "datagrowth-common[supabase]>=0.3.2,<0.4",
]

Editable (desarrollo local del propio paquete)

pip install -e /ruta/a/datagrowth-common

Uso rápido — Supabase Auth en una app FastAPI

Modo API (cliente JS o app móvil envía Authorization: Bearer)

from fastapi import FastAPI

from datagrowth_common import (
    setup_logging, get_logger,
    SupabaseUserDep, require_role,
)
from datagrowth_common.api.users import users_router

setup_logging()
log = get_logger(__name__)

app = FastAPI()

# Endpoints CRUD de usuarios sobre Supabase Admin API:
#   GET    /api/users         (admin, paginado)
#   POST   /api/users/invite  (admin)
#   DELETE /api/users/{id}    (admin)
#   GET    /api/users/me      (cualquier user autenticado)
app.include_router(users_router)


@app.get("/leads")
async def list_leads(user: SupabaseUserDep) -> dict:
    log.info("leads", user=user.id, role=user.role)
    return {"data": [...]}


@app.delete("/leads/{lead_id}")
async def delete_lead(lead_id: str, _: object = require_role("admin")):
    return {"deleted": lead_id}

El cliente envía Authorization: Bearer <jwt> donde el JWT lo emitió la propia Supabase de la app (login local, magic link u OIDC). verify_supabase_jwt valida firma HS256 contra SUPABASE_JWT_SECRET, comprueba exp y aud, y extrae sub, email y app_metadata.role.

Modo server-rendered (Jinja + HTMX + cookie httpOnly)

Para apps donde el navegador navega entre páginas HTML (no SPA):

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse

from datagrowth_common import (
    SessionUserDep, make_auth_router, setup_logging, get_logger,
)

setup_logging()
log = get_logger(__name__)
app = FastAPI()

# Router con login propio + OAuth + refresh + logout. La página HTML de
# /login la sirves tú con tu propio branding; el router solo expone los
# endpoints API.
app.include_router(make_auth_router(
    post_login_redirect="/",
    post_logout_redirect="/login",
))


@app.get("/login", response_class=HTMLResponse)
async def login_page(request: Request, error: str | None = None):
    return templates.TemplateResponse(request, "login.html", {"error": error})


@app.get("/")
async def home(user: SessionUserDep) -> dict:
    return {"email": user.email}

set_session guarda dos cookies (dg_session con el access_token JWT, dg_session_refresh con el refresh_token) httpOnly+Secure+SameSite=Lax. require_session valida la cookie en cada request. Si caduca, llamas a POST /auth/refresh para renovarla.

Variables de entorno

Supabase Auth (v0.2.1+)

Variable Descripción Por defecto
SUPABASE_URL URL del proyecto Supabase (cloud o self-hosted)
SUPABASE_KEY Anon/publishable key (lectura+RLS)
SUPABASE_SERVICE_ROLE_KEY Service role key (Admin API). NO exponer al frontend
SUPABASE_JWT_SECRET Secreto HS256 (Project Settings → API). Usado por verify_supabase_jwt
SUPABASE_JWT_AUDIENCE Audience esperada en el JWT authenticated
DG_APP_URL URL pública de la app (sin slash final). Usada por make_auth_router para construir el redirect_to del callback OAuth derivada de request.base_url si vacío
AUTH_REGISTRATION open permite que cualquiera se registre via POST /auth/register; cualquier otro valor lo bloquea (403) invite_only
AUTH_MAGIC_LINK true habilita POST /auth/magic-link/request (OTP via email) false
DG_ENV development permite cookies sin Secure para tests locales sin TLS production

Genéricas

Variable Descripción Por defecto
DG_ENV development permite cookies sin Secure para tests locales sin TLS production
LOG_LEVEL DEBUG, INFO, WARNING, ERROR INFO
DG_APP_SLUG Slug de la app para el checker de actualizaciones
DG_TENANT_SLUG Slug del tenant para el checker de actualizaciones
DG_APP_VERSION Versión actual de la app 0.0.0
DG_RELEASES_URL URL base del backend de releases https://backend.dev.datagrowth.es

Cookie HMAC propia (auth.session_hmac, opcional)

Variable Descripción Por defecto
DG_SESSION_SECRET Secreto HMAC para firmar la cookie (≥32 bytes)
DG_SESSION_COOKIE Nombre de la cookie dg_session
DG_SESSION_TTL_SEC TTL de la sesión en segundos 86400

Patrón Result

Evita excepciones flotantes retornando el error como valor:

from datagrowth_common import ok, err, Result

async def fetch_data(id: str) -> Result[dict]:
    try:
        data = await some_call(id)
        return ok(data)
    except Exception as e:
        return err(e)

data, error = await fetch_data("123")
if error:
    log.error("fallo fetch", error=str(error))
else:
    print(data)

Todas las funciones auth.supabase_admin.* y verify_supabase_jwt siguen este patrón: nunca lanzan a través de la frontera de módulo.

Respuesta API estándar

from datagrowth_common.api.errors import ApiResponse, ErrorCode, ok_response, err_response

@app.post("/items")
async def create(body: ItemCreate) -> ApiResponse:
    item, exc = await service.create(body)
    if exc:
        return err_response(ErrorCode.INTERNAL_ERROR, "create-failed")
    return ok_response(item)

ApiResponse enforza XOR entre data y error (no se puede tener ambos). El cliente decide éxito leyendo body.error == null.

Checker de actualizaciones

from datagrowth_common.updater.checker import run_periodic_check

def notify(update: dict) -> None:
    print(f"Nueva version disponible: {update['latest']}")

# Llamar en el startup de la app o en un background task
await run_periodic_check(notify, app_slug="fable", tenant_slug="acme")

Tests

pip install -e ".[dev,supabase]"
pytest -q

Cobertura actual (v0.3.0): tests sobre cookie HMAC (auth.session_hmac), validación JWT Supabase, router CRUD de usuarios, cookie httpOnly de sesión Supabase, auth_router (login + OAuth + refresh + logout) y deprecation warning de auth.local.

Versionado y releases

Sigue SemVer. Cambios mayores que rompen API requieren bump major. La release se publica via GitHub Action al hacer git tag vX.Y.Z && git push --tags (ver RELEASING.md).

Versión Cambios principales
v0.3.0 BREAKING. Elimina authentik, auth_fastapi y toda la integración con Authentik OIDC dentro de auth.local. Renombra auth.localauth.session_hmac (alias deprecated). Modelo único: Supabase Auth. Ver CHANGELOG.md para la guía de migración.
v0.2.2 Añade auth.supabase_admin.create_user_with_password y send_password_reset para flujos de gestión end-to-end.
v0.2.1 Añade auth.session (cookie httpOnly server-rendered con refresh) y auth.router (auth_router + make_auth_router con login propio + OAuth Microsoft/Google via Supabase + refresh + logout).
v0.2.0 Añade auth.supabase (verify JWT), auth.supabase_admin (Admin API), api.users.users_router, get_supabase_admin_client. auth.local queda como legacy.
v0.1.0 Primera release pública: auth.local, authentik, auth_fastapi, result, logger, supabase, shared_models, updater.

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

datagrowth_common-0.4.1.tar.gz (47.4 kB view details)

Uploaded Source

Built Distribution

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

datagrowth_common-0.4.1-py3-none-any.whl (60.4 kB view details)

Uploaded Python 3

File details

Details for the file datagrowth_common-0.4.1.tar.gz.

File metadata

  • Download URL: datagrowth_common-0.4.1.tar.gz
  • Upload date:
  • Size: 47.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for datagrowth_common-0.4.1.tar.gz
Algorithm Hash digest
SHA256 746f1f2e63a4bb98c9c480508442da0c5691b8ae771a46dd0fdc7217cf7ea5a2
MD5 f6aedc30f5a0a5ff6761cabbf15ace4c
BLAKE2b-256 6885abd70dab45c626bdd0ea6167283e2cc7f8d11dfa0c304182a13141799cb3

See more details on using hashes here.

Provenance

The following attestation bundles were made for datagrowth_common-0.4.1.tar.gz:

Publisher: release.yml on datagrowth/datagrowth-common

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file datagrowth_common-0.4.1-py3-none-any.whl.

File metadata

File hashes

Hashes for datagrowth_common-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a76fc5207df0ce973c1df381260c9e4b8cebcac4be3bb0424a53fcdfbad1e613
MD5 9f20997d13c2ecc4cbbb12ad7a3cb2c5
BLAKE2b-256 60290d7f66f41a04c1f584570cdc01386a7378c09969b2aab045501ec71be9e8

See more details on using hashes here.

Provenance

The following attestation bundles were made for datagrowth_common-0.4.1-py3-none-any.whl:

Publisher: release.yml on datagrowth/datagrowth-common

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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