| → / Space | Next slide |
| ← | Previous slide |
| Home | First slide |
| End | Last slide |
| Swipe | Touch navigation |
Your host application authenticates users via your own identity provider. The backend mints scoped Databricks tokens on their behalf. Viewers never see Databricks, never need a Databricks account.
Companion to the Cross-IdP Federation deck. That deck gives viewers a real Databricks identity with automatic row-level security. This deck gives viewers no Databricks identity at all.
Why a foreign IdP surfaces a second, unfamiliar sign-in screen inside the embedded iframe.
Basic Embedding versus Embedding for External Users — who authenticates to Databricks, and how.
Three actors, three server-side API calls — the viewer’s browser never touches Databricks directly.
How per-viewer data narrowing is enforced — and where the platform does not enforce it for you.
What to confirm before you build, and the operational details that are easy to miss.
The second login is unbranded and unfamiliar. Viewers on your platform — internal staff, partners, downstream clients — now face an unexpected authentication detour inside your app.
Custom React app (or other platform). All users authenticate once via your own identity provider (Okta, Azure AD, Auth0, or in-house).
Its account-level SSO is configured against a different IdP — not the one your host app uses.
Dashboard is embedded via iframe. The viewer’s browser is asked to authenticate directly to Databricks’ IdP — not the host app’s IdP.
This is expected behavior of basic embedding, not a platform defect. Basic embedding authenticates viewers directly against the account’s IdP. The fix is switching to a different embedding model, not reconciling the two IdPs.
| Basic Embedding | Embedding for External Users | |
|---|---|---|
| Who authenticates to Databricks | The viewer, directly, against the account’s SSO IdP | The app’s own server, using its own Databricks identity |
| Viewer needs a Databricks identity? | Yes — this is the second login prompt | No |
| Where the prompt appears | Inside the iframe, whenever the IdP session is stale | Never — token is injected server-side |
| Data access model | Viewer’s own UC permissions | SP’s UC permissions, narrowed per-query by an app-supplied parameter |
| Setup | Share dialog → generate embed code | Publish the dashboard, create an app identity in Databricks, add server code that requests tokens |
If the host app’s IdP differs from the Databricks account’s SSO IdP, Basic Embedding cannot avoid a second login — that is how the model is designed to work. The fix is switching to a model where the viewer’s Databricks identity is never in play.
Your host application has two parts: the page viewed in a browser (frontend), and a server running your code (backend). The backend is invisible to viewers — it’s the only part that ever authenticates to Databricks. Below is the full flow.
The viewer’s browser talks to your app and to Databricks’ dashboard viewer. It never sends credentials anywhere, never sees a login prompt, never even knows Databricks exists.
Viewers never authenticate to Databricks. The browser uses a scoped token you hand it, talks to your app, and loads the dashboard. No login screen.
The Service Principal is Databricks’ only identity. Add downstream clients, partners, or new user cohorts to your app without SCIM-syncing a single user into Databricks.
The token exchange doesn’t care which IdP signed in your viewers. Okta, Azure AD, Auth0, custom — from Databricks’ side, the flow is identical.
Your viewers may sit in different identity tenants, different organizations, or have no centralized identity at all. Account-level federation and JIT provisioning tools have limits when viewers span multiple tenants. This pattern has no tenant or federation constraint.
Your app owns the viewer’s entire identity. Scoping is a single attribute (client ID, region, department). Dashboards only, no Genie Agents.
Viewers are SCIM-synced to Databricks with real current_user() identities. Unity Catalog row filters fire automatically. Genie Agents, SQL, Dashboards all work.
Viewers stay entirely within your app’s domain. No redirects, no Databricks-branded URLs, no third-party prompts visible to your users.
When your host app and Databricks already use the same IdP, there is no second identity. Basic Embedding is simpler and gives viewers their own real permissions.
Verified against Databricks’ reference implementation. This is server code — the viewer’s browser never runs any of it.
POST {workspace}/oidc/v1/token Authorization: Basic base64(client_id:secret) grant_type=client_credentials&scope=all-apis
GET {workspace}/api/2.0/lakeview/
dashboards/{id}/published/tokeninfo
?external_viewer_id={email}
&external_value={scoping_attr}
Authorization: Bearer {step_1_token}
POST {workspace}/oidc/v1/token Authorization: Basic base64(client_id:secret) grant_type=client_credentials &authorization_details={step_2_response}
authorization_details is Step 2’s response, passed through unmodified. Response: {access_token, token_type, expires_in, created_at}. access_token goes to the embedding SDK — never to the browser as anything else.
external_viewer_id is audit-only. It does not scope access by itself — only external_value, matched in dashboard SQL, does.
SELECT customer_id, region, total_revenue FROM sales.customer_data WHERE department = __aibi_external_value
If a query forgets the __aibi_external_value filter, that viewer sees everything the SP can see. There is no platform-level backstop — every query has to be reviewed.
Publish-time decision: individual data permissions (SP holds the union of access, filtering does the narrowing) vs. shared data permissions (the publisher’s access applies to every viewer uniformly — wrong choice whenever viewers must see different data).
current_user() = the humancurrent_user() would return the SP, not the humanThe embedding APIs only work against a published dashboard, not a draft.
Not shared. This is the setting that makes per-viewer scoping possible at all — and it’s a republish to change later, not a toggle.
What column or value distinguishes one viewer’s data from another’s? Where does that scoping value live: in an IdP claim, your app’s user table, or a Databricks-side lookup table?
Can View on the dashboard, Can Use on the warehouse, UC SELECT across everything any viewer might need — the ceiling that external_value then narrows.
Workspace Settings → Security → External Access → Embed Dashboards. Add your app’s domain to the allowed list. Specify subdomains if needed (e.g., *.example.com).
The embedding SDK relies on them. If your viewer base runs strict browser policies that block third-party cookies, test this before deployment.
| Issue | What to know |
|---|---|
| “Ask Genie Agents” requires basic embedding | Conversational dashboard search (Genie Agents) works under basic embedding. For external-user embedding, use static or parameterized dashboards. |
| Token TTL ~1 hour | The SDK calls getNewToken() ~5 min before expiry. Your backend must re-run the full 3-step token exchange; there is no “refresh token” shortcut. |
| SP blast radius | The SP holds the union of every viewer’s access. If credentials leak, all tenant data is exposed unless every dashboard query enforces the __aibi_external_value filter. |
| Query review is mandatory | There is no platform-level backstop. If a dashboard query forgets the scoping filter, that viewer sees everything the SP can see. Every query must be reviewed. |
| SP credentials stay server-side | client_id and client_secret never leave your backend. Only the minted embed_token is sent to the browser. |
| Publish-time decision | Individual vs. shared data permissions is set when you publish. Changing it later requires republishing the dashboard, not toggling a setting. |