Saltar a contenido

Integrar la aplicación anfitriona#

Esta página es para quien programa la aplicación anfitriona: cómo pide su servidor el token a Empowerment Center, cómo se pone el iframe en la página y cómo se habla con él. Hay dos formas: con el SDK de ECenter, que lo resuelve casi todo, o a mano con el protocolo de mensajes.

Antes de empezar#

  • La aplicación anfitriona tiene que estar dada de alta en Administración › Incrustación, con el origen de la página, el contenido permitido, los roles de los lectores y una credencial activa (véase Incrustación).
  • La aplicación anfitriona ya sabe quién es su usuario: ella lo autentica y decide para qué usuario de ECenter pide el token. El login en ECenter tiene que coincidir con el del usuario de la anfitriona, o la anfitriona tiene que saber traducirlo.
  • La pestaña Probar de la aplicación da un fragmento HTML de ejemplo y enseña los mensajes del iframe: úsela antes de programar.

Atención

El token se pide siempre desde el servidor de la anfitriona, nunca desde el navegador. La credencial de incrustación permite pedir un token para cualquier lector: si llegara al navegador, cualquiera podría verlo todo como otro usuario.

El recorrido#

  1. La página carga el iframe https://ecenter.example.org/embed/<clave>. ECenter solo deja enmarcarlo desde los orígenes de la aplicación.
  2. El iframe avisa a la página con el mensaje ready.
  3. La página pide un token a su propio servidor.
  4. El servidor de la anfitriona pide el token a ECenter con su credencial (POST /api/embed/v1/tokens) para el usuario que ha entrado.
  5. La página da el token al iframe con el mensaje token.
  6. El iframe canjea el token por una sesión incrustada y enseña el contenido.
  7. Cuando la sesión llega a su máximo, se cierra o se recarga el iframe, este manda tokenRequired y se repite desde el paso 3.
Cómo se abre un documento incrustado Secuencia de la incrustación: la página anfitriona carga el iframe, pide un token a su servidor, que lo obtiene de ECenter con la credencial de incrustación; el iframe canjea el token por una sesión incrustada y abre el documento con la identidad del usuario. La credencial solo vive en el servidor anfitrión. carga /embed/{clave} frame-ancestors = orígenes de la aplicación ready pide un token para su usuario POST /api/embed/v1/tokens (credencial Incrustación) token (5 min, un solo uso) token postMessage token canjea el token sesión incrustada abre el documento con la identidad del usuario tokenRequired (al llegar al máximo; vuelve al paso 3) la credencial solo vive en el servidor anfitrión Servidor anfitrión Página anfitriona navegador iframe /embed/{clave} ECenter
Cómo se abre un documento incrustado Secuencia de la incrustación: la página anfitriona carga el iframe, pide un token a su servidor, que lo obtiene de ECenter con la credencial de incrustación; el iframe canjea el token por una sesión incrustada y abre el documento con la identidad del usuario. La credencial solo vive en el servidor anfitrión. carga /embed/{clave} frame-ancestors = orígenes de la aplicación ready pide un token para su usuario POST /api/embed/v1/tokens (credencial Incrustación) token (5 min, un solo uso) token postMessage token canjea el token sesión incrustada abre el documento con la identidad del usuario tokenRequired (al llegar al máximo; vuelve al paso 3) la credencial solo vive en el servidor anfitrión Servidor anfitrión Página anfitriona navegador iframe /embed/{clave} ECenter
Cómo se abre un documento incrustado Ver a tamaño completo Ver a tamaño completo

Pedir el token (servidor anfitrión)#

curl -s -X POST "https://ecenter.example.org/api/embed/v1/tokens" \
  -H "Authorization: Bearer $ECENTER_EMBED_SECRET" -H "Content-Type: application/json" \
  -d '{
        "user": "comercial.norte",
        "target": { "workbook": "5b0e…", "document": "c1f7…" },
        "ui": { "header": true, "theme": "LIGHT" },
        "viewer": "intranet:comercial.norte"
      }'

Respuesta 201:

{ "token": "eyJ…", "expiresAt": "2026-10-17T10:05:00Z", "embedUrl": "https://ecenter.example.org/embed/intranet-comercial" }
Campo Qué es
user El login del usuario de ECenter que va a ver el contenido. Tiene que poder ser lector.
target Qué se incrusta, dentro del contenido permitido: un documento publicado, { "workbook": …, "document": … }, o una plantilla, { "type": "layout", "layout": …, "pages": ["p1", "p2"] } (sin pages, todas las permitidas).
restrictions Opcional. Lo que el token quita (abajo).
ui Opcional. header (la cabecera con el título y la versión, true por defecto), theme (SYSTEM, LIGHT o DARK), page (la página inicial de una plantilla) y memberEvents (avisar a la página de los miembros que pulsa el usuario).
viewer Opcional. Quién es el lector en la anfitriona, solo para la auditoría (se guarda resumido).

Los identificadores del contenido permitido, con sus nombres y las páginas de cada plantilla, los da GET /api/embed/v1/host/app con la misma credencial: no hace falta copiarlos a mano.

El token es un JWT firmado por ECenter que vive 5 minutos y se canjea una sola vez. No lo guarde: pida uno nuevo cada vez que el iframe lo pida.

Restricciones#

"restrictions": {
  "segmentations": [ { "connectionId": "8d6f2c4b-…",
                       "segmentation": { "root": { "type": "group", "logic": "AND", "children": [ … ] } } } ],
  "export": false,
  "versions": "LATEST",
  "bookmark": null
}
Campo Qué hace
segmentations Una segmentación por conexión que se añade con Y a la de la sección del documento. No se puede aplicar a un documento congelado ni a uno de otra conexión: se rechaza con 400 RESTRICTION_NOT_APPLICABLE, nunca se ignora.
export true deja exportar, si la política de la aplicación lo permite.
versions LATEST (solo la última versión publicada) o ALL (cualquiera, si la aplicación deja elegir versión).
bookmark o state La vista inicial: un marcador que el usuario pueda leer, o un estado sin guardar. Como mucho uno de los dos.

Las restricciones solo quitan: lo que el usuario ve lo deciden su identidad y sus privilegios en ECenter.

Errores al pedir el token#

HTTP code Cuándo
401 INVALID_CREDENTIAL La credencial no vale: no existe, ha caducado, se ha revocado o su titular está inhabilitado.
403 NOT_ALLOWED El titular de la credencial ya no tiene Administración del sistema.
403 EMBED_USER_NOT_ALLOWED El usuario no puede ser lector: no existe, está inhabilitado, no tiene un rol de la aplicación ni Acceso al portal, o tiene Administración del sistema.
403 EMBED_CONTENT_NOT_ALLOWED El contenido no está permitido o el usuario no puede leerlo ahora.
403 EMBED_BOOKMARK_NOT_ALLOWED, EMBED_VERSION_NOT_ALLOWED, EMBED_EXPORT_NOT_ALLOWED El marcador, la versión o la exportación no están permitidos.
403 EMBED_APP_DISABLED La aplicación está deshabilitada.
400 RESTRICTION_NOT_APPLICABLE Una segmentación sobre un documento congelado o de otra conexión.
422 INVALID_SEGMENTATION La segmentación no es válida.
403 LICENSE_INVALID Sin licencia válida.
404 EMBEDDING_DISABLED La incrustación está desactivada.
429 RATE_LIMITED La aplicación ha pasado de 600 tokens por minuto. Espere lo que diga Retry-After.

Cerrar las sesiones de un usuario#

Cuando el usuario sale de la anfitriona, su servidor puede cerrar todas sus sesiones incrustadas en la aplicación:

curl -s -X DELETE "https://ecenter.example.org/api/embed/v1/host/sessions?user=comercial.norte" \
  -H "Authorization: Bearer $ECENTER_EMBED_SECRET"
{ "closed": 1 }

Con la misma credencial, GET /api/embed/v1/host/sessions lista las sesiones abiertas de la aplicación y DELETE /api/embed/v1/host/sessions/{ref} cierra una.

Poner el iframe con el SDK#

El SDK es un fichero JavaScript sin dependencias que sirve el propio ECenter: /embed/sdk/v1/ecenter-embed.js. Crea el iframe, le da el token cuando lo pide y vuelve a pedirlo solo cada vez que hace falta.

<div id="ventas"></div>
<script src="https://ecenter.example.org/embed/sdk/v1/ecenter-embed.js"></script>
<script>
  const embed = ECenterEmbed.mount(document.getElementById('ventas'), {
    url: 'https://ecenter.example.org/embed/intranet-comercial',
    title: 'Ventas por zona',
    // Pide el token a SU servidor, que lo pide a ECenter con la credencial
    fetchToken: () => fetch('/ecenter/token', { method: 'POST' })
                        .then((r) => r.json()).then((b) => b.token),
    onEvent: (message) => console.log(message.type),
  });
</script>
Opción de mount Qué es
url La dirección del iframe, https://…/embed/<clave>. Obligatoria.
title El título del iframe, para la accesibilidad. Obligatorio.
fetchToken Una función que devuelve una promesa con el token. Se llama al principio y con cada tokenRequired; recibe { reason }. Obligatoria.
onEvent Recibe cada mensaje válido del iframe y los del SDK (tokenError).
autoResize true por defecto: ajusta el alto del iframe al de una plantilla.
height El alto inicial (480px por defecto).
sandbox, allow Para cambiar el sandbox recomendado y allow="fullscreen".

El objeto que devuelve tiene setFilters(filtros), navigate(página), setTheme('SYSTEM' | 'LIGHT' | 'DARK'), logout(), requestToken() y destroy().

  • Si fetchToken no responde en 15 segundos, el SDK lo da por fallido (tokenError con TOKEN_FETCH_TIMEOUT) y deja pedir otro.
  • Para no entrar en un bucle de rechazos, no pide más de 6 tokens en un minuto.
  • Tras logout() no vuelve a abrir la sesión solo: para otra, requestToken().
  • El SDK admite integrity con crossorigin="anonymous".

Poner el iframe a mano#

Sin el SDK, la página habla con el iframe por postMessage. Todos los mensajes llevan { v: 1, type, … }. Este es el fragmento que da la pestaña Probar:

<iframe id="ecenter" title="Ventas por zona" src="https://ecenter.example.org/embed/intranet-comercial"
        style="width: 100%; height: 600px; border: 0" allow="fullscreen"></iframe>
<script>
  const frame = document.getElementById('ecenter');
  const origin = new URL(frame.src).origin;
  window.addEventListener('message', async (event) => {
    if (event.source !== frame.contentWindow || event.origin !== origin || event.data?.v !== 1) return;
    if (event.data.type === 'ready' || event.data.type === 'tokenRequired') {
      // Su servidor pide el token con la credencial (POST /api/embed/v1/tokens) y se lo da a esta página
      const { token } = await (await fetch('/ecenter/token')).json();
      frame.contentWindow.postMessage({ v: 1, type: 'token', token }, origin);
    }
    if (event.data.type === 'resize') frame.style.height = event.data.height + 'px';
  });
</script>

Reglas de seguridad del protocolo:

  • Mande los mensajes siempre al origen exacto de ECenter, nunca a *.
  • Acepte solo los mensajes cuyo event.source sea el iframe y cuyo event.origin sea el de ECenter.
  • El portador de la sesión incrustada nunca sale del iframe: la página solo ve el token de 5 minutos.
Dirección Mensajes
Página → iframe token (con token), setFilters (filtros maestros: presentación, no seguridad), navigate (con page), setTheme (con scheme) y logout
Iframe → página ready, tokenRequired (con reason), loaded (título, tipo, versión y filtros), resize (con height), filtersChanged, pageChanged, memberActivated (solo con ui.memberEvents), linkOpened, error (con code y message) y sessionEnded (con reason)

logout cierra la sesión con el motivo EMBED_LOGGED_OUT: el iframe manda sessionEnded y no pide otro token.

También se puede dar el token en el fragmento de la dirección (/embed/<clave>#token=…), que no llega al servidor ni al Referer y el iframe borra al leerlo. Nunca en la consulta (?token=) ni en la ruta.

Lo que ve el lector#

Dentro del iframe, el lector ve el documento o la plantilla sin la barra lateral de ECenter: una cabecera reducida opcional (título, versión y exportar, si se permite) y, en una plantilla, las pestañas de sus páginas permitidas, la barra de filtros maestros y sus bloques. Los visores son los del portal, sin SQL.

Mientras espera el token dice «Esperando la autorización de la aplicación…». Si algo falla, enseña un mensaje sin datos de la cuenta:

Lo que ve el lector Causa
«El acceso ha caducado» El token pasó sus 5 minutos sin canjearse.
«El acceso ya se había usado» El token ya se había canjeado: cada autorización sirve una sola vez.
«El acceso no es válido» El token no es de esta aplicación o no se puede verificar.
«La sesión ha terminado» Inactividad, duración máxima o la cerró quien administra.
«La sesión se ha cerrado» La anfitriona mandó logout.
«El contenido no está disponible aquí» El origen de la página no está entre los de la aplicación.
«No tiene acceso a este contenido» El usuario no puede ser lector.
«El contenido no está disponible» El contenido ya no está publicado, la aplicación está deshabilitada, la licencia no vale u otro motivo.

La anfitriona de ejemplo#

El repositorio trae una intranet de ejemplo, sin dependencias (Node 18 o posterior), en tools/demo-data/embed-host/server.mjs. Enseña cómo se pide cada token desde el servidor para el usuario que ha entrado, cómo se renueva solo con el SDK, cómo se cierra la sesión en ECenter al salir de la intranet y cómo se pregunta qué se puede incrustar.

export ECENTER_URL=https://ecenter.example.org      # de donde el navegador carga el SDK y el iframe
export ECENTER_EMBED_APP=intranet-comercial          # la clave de la aplicación
export ECENTER_EMBED_SECRET=ect_…                    # la credencial de incrustación (nunca en la página)
node tools/demo-data/embed-host/server.mjs           # escucha en http://localhost:4300

EMBED_HOST y EMBED_PORT dicen dónde escucha (tiene que ser uno de los orígenes de la aplicación) y EMBED_USERS, qué usuarios pueden «entrar» en la intranet.

Comprobaciones antes de producción#

  • ECenter y la anfitriona van por https, y el origen de la anfitriona está en los de la aplicación.
  • El token se pide desde el servidor de la anfitriona, para el usuario que ella ha autenticado, y nunca se guarda.
  • El secreto de la credencial está en el almacén de secretos del servidor, no en el código ni en la página.
  • Ningún proxy añade X-Frame-Options ni otra Content-Security-Policy a /embed/.
  • Con varias instancias, la afinidad por X-ECenter-Embed-Session.
  • Al salir de la anfitriona se llama a logout() y se cierran las sesiones del usuario.
  • Hay una fecha prevista para rotar la credencial antes de que caduque.

Véase también#