@miniappfy/embed
v0.2.0
Published
Client-side snippet to embed a MiniAppfy mini-app (or a My Grids dashboard) into a third-party website.
Readme
@miniappfy/embed
Client-side snippet to embed a MiniAppfy mini-app into a
third-party website. Ships as a drop-in <script> (UMD/IIFE, global MiniappfyEmbed) and as
ESM/CJS for bundlers.
How it works
The SDK creates an <iframe> pointing at the embed shell served by MiniAppfy at
https://<EMBED_HOST>/e/:appId, then hands it a short-lived embed-JWT over postMessage
(never via URL). The shell trades the JWT for capabilities and renders the mini-app in an opaque,
sandboxed inner frame.
your page ──creates iframe──▶ shell (embed.miniappfy.dev/e/:appId)
▲ │ embed:handshake
│ embed:init { jwt } ◀───────────┘
└──────────────────────────────────▶ (shell verifies JWT → caps)
embed:ready { app } ◀──┘ (or embed:error { error })Message protocol (must match the shell exactly):
- Shell → parent:
{ source: "miniappfy-embed", type: "embed:handshake" }, thenembed:ready(payload.app) /embed:error(payload.error) / optionallyembed:resize(payload.height). - Parent → shell:
{ source: "miniappfy-embed-parent", type: "embed:init", jwt }, posted to the embed origin. The SDK validates that every inbound message comes from its own iframe at the embed origin.
The embed-JWT (minted by YOUR backend)
The token is the embed session token: HS256, signed by your backend with the per-app
signingKey you obtained from MiniAppfy. It is short-lived (exp ≤ 120s), single-use (jti), and
only MiniAppfy verifies it.
Claims:
| claim | required | notes |
| ------------ | -------- | -------------------------------------------------------------- |
| iss | yes | the embed config id |
| aud | yes | "miniappfy-embed" |
| sub | yes | OPAQUE, non-PII end-user id (e.g. a stable hash) |
| jti | yes | random nonce; single-use within the validity window |
| exp | yes | ≤ 120s from now |
| purpose | no | optional consent/purpose tag |
| consentRef | no | optional consent reference |
| legalBasis | no | optional legal basis (LGPD) |
Never put PII in
sub. Never ship thesigningKeyto the browser — mint the JWT server-side.
Example (Node, jose):
import { SignJWT } from "jose";
const jwt = await new SignJWT({ sub: opaqueUserId, purpose: "appointments" })
.setProtectedHeader({ alg: "HS256" })
.setIssuer(EMBED_CONFIG_ID) // iss
.setAudience("miniappfy-embed") // aud
.setJti(crypto.randomUUID()) // single-use
.setExpirationTime("60s") // ≤ 120s
.sign(new TextEncoder().encode(SIGNING_KEY));Expose this via a small endpoint your page can fetch (authorize with your own session/cookies):
// GET /api/miniappfy/token → { "token": "<jwt>" } (or the raw JWT as text)Usage — declarative (drop-in <script>)
<div
data-miniappfy-app="clxxxxxxxxxxxxxx"
data-token-url="/api/miniappfy/token"
style="max-width: 720px"
></div>
<script
src="https://unpkg.com/@miniappfy/embed/dist/embed.js"
data-miniappfy-app="clxxxxxxxxxxxxxx"
data-token-url="/api/miniappfy/token"
></script>The script auto-initializes every [data-miniappfy-app] element on DOMContentLoaded.
Supported data-* attributes: data-miniappfy-app (required), data-token-url, data-token,
data-embed-host, data-title, data-target (CSS selector of the mount point).
Usage — imperative (bundler / ESM)
import { MiniappfyEmbed } from "@miniappfy/embed";
const embed = new MiniappfyEmbed({
target: "#app", // HTMLElement or CSS selector
appId: "clxxxxxxxxxxxxxx",
tokenUrl: "/api/miniappfy/token", // or: token / getToken()
onReady: (app) => console.log("ready:", app.name),
onError: (err) => console.error("embed error:", err),
});
// later
embed.destroy();Resolving the JWT
Provide exactly one of:
tokenUrl— the SDK doesfetch(tokenUrl, { credentials: "include" })and accepts either{ token }JSON or the raw JWT as text (override withtokenFetchInit).token— a pre-minted JWT string.getToken()— a function returning a JWT (sync or async); called on every handshake, so it can return a fresh token each time.
Options
| option | type | default |
| ---------------- | ----------------------------- | ----------------------------- |
| target | HTMLElement \| string | — |
| appId | string | — |
| embedHost | string | https://embed.miniappfy.dev |
| tokenUrl | string | — |
| token | string | — |
| getToken | () => string \| Promise<…> | — |
| tokenFetchInit | RequestInit | { credentials: "include" } |
| title | string | "MiniAppfy mini-app" |
| onReady | (app) => void | — |
| onError | (error: string) => void | — |
License
MIT
