Fejlesztői dokumentáció

Beágyazott oldalak

Az app saját felülete a Dynex bal menüjéből nyílik, iframe-ben. Az oldalad egy aláírt kontextus-tokenből tudja, ki nézi, és egy postMessage hídon beszél a kerettel.

Kb. 9 perc olvasás

Beágyazás és a híd

Az app minden embeds[] bejegyzése egy menüpont a bérlő bal menüjében. Rákattintva a Dynex az /app/apps/<slug>/… oldalon egy teljes magasságú iframe-ben tölti be az app_url + path címet, a query-ben egy aláírt kontextus-tokennel:

1https://app.example.com/dashboard2  ?dynex_token=<base64url(payload)>.<base64url(HMAC-SHA256(embed_secret, base64url(payload)))>3  &dynex_theme=light|dark        # az induló téma, hogy az első festés se villanjon

Két token, egy formátum. A query-ben (dynex_token) érkező token PII-mentes: csak az azonosítókat hordozza, a tenantName, userEmail és userRole mezője üres string - az iframe URL az app hozzáférési naplójába és a Refererbe kerül, oda ezek nem valók. A teljes kontextus tokenje a hídon (dynex:context, a dynex:ready után azonnal, majd 4 percenként) és a POST /api/apps/embed/verify válaszában jön - a nevet, e-mailt, szerepet onnan vedd (bridge.onContext).

A kontextus-token mezői

Payload-mezőJelentés
installIdA telepítés azonosítója (= a token-csere install_id-ja).
tenantIdA bérlő azonosítója.
tenantNameA bérlő neve. A query-tokenben üres (a hídon és a verify-válaszban kitöltve).
userIdA Dynex-felhasználó azonosítója, aki az oldalt nézi.
userEmailA felhasználó e-mail címe. A query-tokenben üres (csak a hídon).
userRoleA felhasználó szerepe a bérlőnél (pl. owner, admin, staff). A query-tokenben üres (csak a hídon).
localeNyelv (jelenleg hu).
themelight | dark - kövesd, hogy az iframe ne üssön el a kerettől.
appSlugAz app slugja.
iatKiadás (unix mp).
expLejárat (unix mp): a kiadástól 5 perc. Ellenőrzéskor 30 mp óraeltérés megengedett.
nonceEgyszeri véletlen - visszajátszás-védelemhez az app oldalán.

A token ellenőrzése és kezelése

Ellenőrzés: helyben a verifyEmbedTokenForInstall(token, getEmbedSecret)-tel (a token telepítésének tárolt embed_secret-jével), vagy a Dynexen a POST /api/apps/embed/verify végponton ({ client_id, client_secret, token } → { ok, context, exp, nonce }), ami azt is megnézi, hogy a telepítés még aktív-e. A helyi ellenőrzés mellé ajánlott a rendszeres távoli ellenőrzés (pl. minden új sessionnél), mert az eltávolítást vagy felfüggesztést csak a Dynex látja. A token rövid életű: a betöltés utáni saját sessionödet te kezeled, ne a tokenre építs.

  • A token a query-ben érkezik: PII-mentes, de azonosítókat hordoz (tenantId, userId, installId) és bemutatásra jogosít: ne naplózd (a hozzáférési naplóból maszkold a dynex_token-t), ellenőrzés után azonnal válts saját sessionre, és 302-vel irányíts a token nélküli URL-re, hogy ne maradjon az előzményekben és a Refererben.
  • A kontextus mezői (tenantName, userEmail, …) bérlői bevitel: HTML-be csak escape-elve írd ki, különben tárolt XSS az app originjén.
  • Az egyszeri nonce ellenőrzése nem elvárás - a védelem az aláírás és a 5 perces lejárat. Ha mégis egyszer fogadod el, tartsd a nonce-t csak a lejáratig, és az élő sessiont ne dobd el: a Dynex keret újratöltése ugyanazzal a tokennel jöhet.
  • Iframe-ben a saját sütid csak SameSite=None; Secure-ral megy; Safari a harmadik-fél sütit alapból blokkolja - ott a Storage Access API vagy a hídon 4 percenként érkező friss token a megoldás.

A postMessage híd

Mindkét irány kizárólag a másik fél originjével megy (targetOrigin és event.origin ellenőrzés), és az app csak a közvetlen szülő ablak (event.source === window.parent) üzenetét fogadja el. Az üzenetek JSON-objektumok, a type mező a diszkriminátor.

IrányÜzenetMezőkHatás
app → Dynexdynex:ready-Az app betöltött; a Dynex válaszul dynex:context-et küld.
app → Dynexdynex:resizeheightAz iframe magasságának beállítása (px).
app → Dynexdynex:navigatepathNavigálás a Dynexen belül (pl. /app/reservations); válasz: dynex:navigate-result.
app → Dynexdynex:toastkind, messageÉrtesítés a keretben (success | error | info | warning).
app → Dynexdynex:getContext-Kontextus újrakérése; válasz: dynex:context.
app → Dynexdynex:openExternalurlKülső URL megnyitása új lapon.
Dynex → appdynex:contextcontext, tokenA kontextus (a fenti payload mezői) és egy friss token.
Dynex → appdynex:themethemeTéma-váltás (light | dark).
Dynex → appdynex:navigate-resultok, path, error?A navigálás eredménye.
A híd a böngészőben
1import { createAppBridge } from "@dynex/app-sdk/browser";2 3// A Dynex origin a SZERVER konfigjából (pl. <meta name="dynex-origin">), ne a referrerből:4// egy idegen oldalba ágyazott másolatnál a referrer a támadó originje lenne.5const dynexOrigin = document.querySelector('meta[name="dynex-origin"]')?.content;6const bridge = createAppBridge({ dynexOrigin }); // kihagyva: a dynex_origin query-paraméter, majd a referrer - csak https://*.dynex.hu számít, különben prod.dynex.hu7bridge.onContext(({ context }) => render(context)); // context.tenantName, userEmail: escape-elve jelenítsd meg8bridge.onTheme((theme) => document.documentElement.dataset.theme = theme);9bridge.autoResize();                           // ResizeObserver → dynex:resize10bridge.ready();11saveButton.onclick = () => bridge.toast("success", "Elmentve.");12link.onclick = () => bridge.navigate("/app/reservations");

Keret-szabályok (CSP)

A Dynex az app-oldalakon frame-src 'self' https: CSP-fejlécet küld (fejlesztői buildben http://localhost:* is engedett), a valódi kapu az alkalmazás-réteg: az iframe csak az aktív, jóváhagyott (vagy saját sandbox-) telepítés app_url-jét tölti be, és a keret minden üzenet előtt ellenőrzi, hogy az iframe originje az appé. Élesben ezért a sandbox-telepítéshez is https app_url kell (pl. ngrok / cloudflared alagút). Felfüggesztett app menüpontja eltűnik, és az iframe nem töltődik be.

A másik irány a te dolgod

A beágyazott oldalad küldjön Content-Security-Policy: frame-ancestors https://prod.dynex.hu fejlécet, hogy idegen oldal ne ágyazhassa be.

Beágyazás-próba a portálon

A fejlesztői portálon az app „Beágyazás-próba” fülén a beágyazott oldalad a teszt-fiókodban töltődik be, ugyanazzal a kerettel, eredet-ellenőrzéssel és üzenet-szabályokkal, mint élesben. A navigációt és a külső linkeket a próba nem hajtja végre, csak naplózza.

  • Mi kell hozzá: kész teszt-fiók, az app telepítve a teszt-fiókra (a kontextus-token a telepítés titkával van aláírva), és megadott app_url.
  • Oldal, al-útvonal, újratöltés: új keret töltődik be, új tokennel, ahogy élesben egy menüpontra kattintva.
  • Téma, nyelv, szerepkör: a keret marad, az appod új dynex:context üzenetet kap (témaváltásnál dynex:theme is megy) - így az élő frissítést próbálhatod ki.
  • Keret szélessége: 390 px, 768 px vagy teljes szélesség, a telefonos és a táblagépes elrendezés ellenőrzéséhez.
  • Üzenetnapló: minden üzenet a keret és a Dynex között, a legfrissebb felül. A tokenek maszkolva szerepelnek, a napló csak a böngésződ memóriájában él, és JSON-ként kimásolható.

Helyi fejlesztés és a böngészők

Amíg az app nincs beküldve, az app_url lehet http://localhost cím is; beküldéshez nyilvános https cím kell. Hogy a helyi cím hol töltődik be, az a kerettől és a böngészőtől függ:

Holhttp://localhostMit tegyél
Beágyazás-próba, Chrome és FirefoxBetölt.Nincs teendő.
Beágyazás-próba, SafariNem tölt be: a Safari a https oldalba ágyazott http címet vegyes tartalomként letiltja.Használj HTTPS-alagutat (pl. cloudflared, ngrok), és azt add meg app_url-nek.
A Dynex alkalmazás kerete (/app/apps/…), élesbenNem tölt be: a keret csak https címet enged.HTTPS-alagút, a teszt-telepítéshez is.
  • Alagút mögött a beágyazott oldalad https-en fut, ezért a saját sütidnek SameSite=None; Secure kell, ahogy élesben.
  • A híd originjét ilyenkor is a szervered konfigjából add meg (dynexOrigin), ne a referrerből.
  • Az alagút indításának lépései a Webhookok és események oldalon vannak; ugyanaz az alagút szolgálja ki a webhookot és a beágyazott oldalt.