Embedded Partner Portal
Drop your partner’s real portal — their rooms, deals, resources, contacts, everything — straight into your own app, inside an iframe. The partner is signed in automatically with a short-lived token your backend mints, so there’s no second login. The frame auto-resizes to its content and reports navigation back to your page.This is not the same as embedding lead forms.
Lead forms are a public, unauthenticated capture surface loaded with a script
tag and a form UUID. The partner portal is an authenticated product
surface: it requires an SSO token minted by your backend, the
@journeybee/embed SDK, and
an allowlisted host. The two are completely separate integrations.Embedding requires HTTPS on both the host page and the portal. The portal
session cookie is
SameSite=None; Secure, which browsers refuse to set on
insecure origins. Use a tunnel (e.g. ngrok) for local testing.Prefer a running example? The
Journeybee Embed Examples
repo is a live playground + copy-paste snippets (forms + portal), including a
backend token-mint reference — clone it,
pnpm install, pnpm dev.How it works
- Your backend authenticates the user (you already do this) and mints a short-lived, single-use SSO token signed with your embed signing key.
- The
@journeybee/embedSDK mounts an iframe pointing at the portal, carrying your public company id and the token. - The portal verifies the token server-side, establishes a session, and hands off to the partner’s portal — signed in, in embed mode.
- The SDK wires a postMessage bridge: auto-resize, navigation events, sign-out.
companies.uuid), not
by the URL the portal is served from. Embedding does not require a custom
domain — it works on the shared portals.journeybee.io or your own custom
domain, independently.
Prerequisites (one-time, in Journeybee)
- Enable the Embedded Portal module — Settings → Portal Settings.
- Copy your company id (the public
companies.uuidshown there). You pass it to the SDK ascompanyand set it as the token’siss/aud. - Add the hostname of the page that will host the iframe to Embedded
Portal Domains (e.g.
app.acme.com). The portal refuses to render in a frame whose ancestor isn’t allowlisted (frame-ancestorsCSP). - Create an Embed Signing Key under Embed Signing Keys. The secret
(
jb_embed_…) is shown once — store it as a server-side secret.
Install
Published on npm —@journeybee/embed:
Quick start
1. Mint a token on your backend
Run this on your server, after you’ve authenticated the user. Never mint a token in the browser — anyone with the signing key can impersonate any user.2. Mount the embed on the client
portalHost is just where the portal is
served; company identifies which tenant — they’re independent.
The createPortalEmbed call
It returns a
PortalEmbed:
Events
Subscribe withembed.on(type, listener):
React
Re-authentication
A token is single-use and short-lived; the resulting session lasts much longer. When the session is missing or expired, the embed emitsportal:authRequired —
mint a fresh token and re-mount:
Security model
- No secrets cross the iframe boundary.
portal:authenticatedis a bare signal — never a token or session. The session lives in anHttpOnlycookie the portal sets itself. - Tokens are backend-minted only, short-lived (
exp ≤ iat + 120s), single-use (jtireplay-guarded), and audience/issuer-bound to your company id. The signing secret never reaches the browser. - Origin allowlisting is enforced server-side: the portal only frames inside hosts you’ve added to Embedded Portal Domains.
- Public ids only. The token and all client code use the public
companies.uuid; internal numeric ids are never exposed. - Storage Access. In browsers that partition third-party storage (e.g.
Safari), the portal prompts once for Storage Access so its session cookie is
delivered inside the iframe. The SDK marks the iframe
allow="storage-access".