How Superset Embedded SDK actually works
guest tokens, RLS, and the config nobody documents in one place
A walkthrough of the official @superset-ui/embedded-sdk: the iframe lifecycle, the guest token flow, row-level security, and every config switch you need to flip.

Sooner or later most SaaS products need charts inside them, and almost nobody wants to build a query engine and a filter UI from scratch to get there. So you reach for a BI tool. We reach for Apache Superset: SQL-first, open source, no per-seat license. Then you embed it.
The official way in is the
@superset-ui/embedded-sdk
npm package. It drops a Superset dashboard into a sandboxed, cross-origin
<iframe> and handles authorization, messaging, and basic rendering. The
pitch is true as far as it goes: a working dashboard in five lines of code.
What the readme leaves out is what those five lines actually start. An
iframe. A guest-token flow. Row-level security. A config surface that runs
well past five lines. We've shipped this in production, so here's the whole
machinery before you commit to running it.
The client side: what embedDashboard() actually does
The host application initializes the embed by calling embedDashboard()
with a DOM mount point, the Superset domain, the dashboard UUID, and an
authentication callback:
import { embedDashboard } from "@superset-ui/embedded-sdk";
embedDashboard({
id: "abc12345-6789-abcd-ef01-23456789abcd", // Dashboard UUID from the Embed modal
supersetDomain: "https://analytics.example.com",
mountPoint: document.getElementById("analytics-container"),
fetchGuestToken: () => fetchTokenFromBackend(),
dashboardUiConfig: {
hideTitle: true,
filters: { expanded: false },
},
iframeSandboxExtras: ["allow-top-navigation", "allow-popups-to-escape-sandbox"],
referrerPolicy: "same-origin",
});When this executes, six things happen in sequence:
- The SDK constructs and mounts an
<iframe>inside the designated container. - The iframe's
srcpoints at the/embedded/<uuid>route on the Superset server. - To authorize the cross-origin request without a login screen, the SDK
calls your
fetchGuestTokencallback. - The callback fetches a short-lived JWT — the guest token — from your application's backend.
- On load, the SDK sets up an HTML5
MessageChanneland instantiates a bidirectional communication layer via the@superset-ui/switchboardpackage. - The guest token is transmitted over the
postMessagechannel to authorize the session inside the isolated iframe.
The important architectural fact: from this moment on, everything the user does — filtering, drilling, navigating tabs — happens inside an independent browser context your application cannot see into.
The guest token flow and row-level security
Superset never issues guest tokens directly to client browsers. Your
backend acts as a trusted intermediary, executing a server-to-server POST
to /api/v1/security/guest_token/, authenticated with a privileged service
account. The payload defines a temporary identity, the resources it may
access, and optional row-level security clauses:
{
"user": {
"username": "customer_user_987",
"first_name": "Jane",
"last_name": "Doe"
},
"resources": [
{ "type": "dashboard", "id": "abc12345-6789-abcd-ef01-23456789abcd" }
],
"rls": [
{ "clause": "organization_id = 123" }
]
}Superset validates the request, signs the JWT with GUEST_TOKEN_JWT_SECRET,
and returns it to your backend, which passes it to the client. When the
embedded endpoint receives the token, Superset instantiates an anonymous
session mapped to the role configured in GUEST_ROLE_NAME (default:
Public). The RLS clauses are compiled and appended to every SQL query the
dashboard runs.
Two things about this design bite you later:
- It is a second authentication system. The guest token has nothing to do with your host application's SSO session. You now operate two parallel auth flows in one browser context.
- RLS is synthetic. Access rules are evaluated against an anonymous guest role plus whatever clauses your backend injected — not against the real authenticated user's claims.
The full configuration surface
Production setups require coordinated changes across superset_config.py,
HTTP headers, and dashboard metadata. This is the complete checklist:
| Parameter | Where | Purpose | Requirement |
|---|---|---|---|
EMBEDDED_SUPERSET | superset_config.py | Feature flag exposing guest token + embedded endpoints | Must be True |
GUEST_TOKEN_JWT_SECRET | superset_config.py | Signs and verifies guest JWTs | Must be changed from default in production |
GUEST_TOKEN_JWT_EXP_SECONDS | superset_config.py | Guest token validity | Default 300s (5 min) |
GUEST_ROLE_NAME | superset_config.py | FAB role assigned to guest users | Default Public, often overridden to Gamma |
ENABLE_CORS | superset_config.py | Cross-origin resource sharing | Must be True |
CORS_OPTIONS | superset_config.py | Allowed origins, exposed headers, credentials | Must explicitly include host origins with credentials |
TALISMAN_CONFIG | superset_config.py | Relaxes iframe security headers | Needs custom content_security_policy with frame-ancestors |
X-Frame-Options | HTTP header / nginx | Blocks iframe rendering by default | Must be overridden or managed via CSP |
| Allowed Domains | Dashboard metadata | Restricts which origins may embed this dashboard | Checked against Referer; must be allow-listed |
Five lines of client code, nine server-side switches. Most embedding
failures we get called about trace back to one of the last four rows —
CORS, Talisman, X-Frame-Options, and the per-dashboard allow-list
interact in non-obvious ways, especially behind reverse proxies.
Where the architecture stops
Everything above works as documented, and for internal tools, prototypes, and low-stakes reporting the SDK is a perfectly reasonable choice. But the iframe boundary is structural, and no configuration flag crosses it. An isolated cross-origin frame fundamentally cannot:
- share its filter state with the host application's router or URL;
- reuse the host's existing SSO session (the guest token is a second, parallel system);
- adapt to host-controlled CSS or dynamic dark mode without loading flashes;
- let parent DOM elements render on top of charts, tooltips, or dropdowns;
- give you editorial control over which dashboards appear for which users without shipping frontend updates.
Each of these turns into a concrete, documented pain point once embedded analytics becomes a real product feature rather than an internal convenience. We collected the six most common failure modes — with the GitHub issues to prove they are not edge cases — in the next article of this series.
If you want to see what dashboards embedded without an iframe look like, the interactive demo on our Superset Embedded page runs the production microfrontend build, and the Superset hub covers the rest of the fork.
Also on X
A shorter, sharper take in article form: x.com/RtKazakov.