--- name: sign_metabase_embed_jwt kind: function lang: py domain: infra version: "1.0.0" purity: impure signature: "def sign_metabase_embed_jwt(secret: str, resource_type: str, resource_id: int, base_url: str, params: dict | None = None, exp_seconds: int = 3600, theme: str | None = None, bordered: bool = True, titled: bool = True) -> dict" description: "Firma un JWT de static-embedding de Metabase (HS256 con PyJWT) y construye la URL del iframe (/embed//#opciones). Soporta question y dashboard, params locked/enabled, TTL configurable y tema opcional." tags: [metabase, embed, jwt] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: ["jwt", "time"] tested: true tests: ["test_firma_y_decodifica_round_trip", "test_resource_type_invalido_lanza_valueerror", "test_embed_url_dashboard"] test_file_path: "python/functions/infra/sign_metabase_embed_jwt_test.py" file_path: "python/functions/infra/sign_metabase_embed_jwt.py" params: - name: secret desc: "Secret de embedding de Metabase (Settings > Embedding). NUNCA va al cliente." - name: resource_type desc: '"question" o "dashboard" — tipo del recurso a embeber.' - name: resource_id desc: "Id numerico de la card o dashboard en Metabase." - name: base_url desc: 'URL base de la instancia, ej. "https://reports.autingo.es".' - name: params desc: "Parametros de embedding locked/enabled (dict). Default {}." - name: exp_seconds desc: "TTL del token en segundos desde ahora. Default 3600 (1h)." - name: theme desc: 'Tema opcional: "night" | "transparent" | None.' - name: bordered desc: "Si el iframe muestra borde. Default True." - name: titled desc: "Si el iframe muestra titulo. Default True." output: 'dict con {"token": jwt str, "embed_url": url completa /embed//#opciones, "exp": unix int}.' --- ## Ejemplo ```python import sys, os sys.path.insert(0, os.path.join("python", "functions")) from infra import sign_metabase_embed_jwt # El secret lo pasa el caller (p.ej. desde pass: metabase/aurgi-embed-secret) result = sign_metabase_embed_jwt( secret=os.environ["MB_EMBED_SECRET"], resource_type="question", resource_id=8048, base_url="https://reports.autingo.es", ) print(result["embed_url"]) # https://reports.autingo.es/embed/question/#bordered=true&titled=true print(result["exp"]) # unix timestamp de expiracion ``` ## Cuando usarla Cuando necesites embeder una card/dashboard de Metabase en un iframe sin exponerla publicamente: firma server-side en cada carga, nunca hardcodees el token. ## Gotchas - El `secret` NUNCA va al cliente — la firma se hace server-side y solo viaja el token al iframe. - El token expira (`exp_seconds`, default 1h). Refirma en cada carga; no caches el token. - La card/dashboard necesita `enable_embedding=true` en Metabase, o el endpoint de embed devuelve error. - Los `params` deben estar registrados en `embedding_params` como `locked` o `enabled` en Metabase, o se ignoran silenciosamente. - Verificado contra reports.autingo.es: el endpoint `/api/embed/card//query` devuelve 202 + filas con token valido, 400 con token manipulado.