Databricks — Cross-IdP Federation
01 / 16

Navigation

→ / SpaceNext slide
←Previous slide
HomeFirst slide
EndLast slide
SwipeTouch navigation
Click anywhere or press Esc to close

Cross-IdP Federation

Bringing external, foreign-IdP users into Databricks-governed apps and analytics — without a second login and without losing per-user governance.

The token exchange mechanics, the two architecture choices, and how to implement it.

Agenda

What this deck covers.

1
The issue

External users need governed access, without a Databricks account and without a fragile app-level ACL layer.

2
What it is

The two-leg token exchange mechanic, and the two architecture choices it supports.

3
Why it matters

Scale, security, and compliance — concretely, not hand-wavy.

4
How to implement it

Actors, prerequisites, federation policy config, and the gotcha that trips up almost every deployment.

5
A go/no-go checklist

What to verify before this goes live.

The issue

External users need governed access. Neither obvious option works well.

Channel partners, external auditors, and consultants need to query Databricks-backed data through an app or dashboard. They authenticate through their own identity provider — Auth0, Okta, Entra, or whatever their organization uses. They are not in your corporate directory, and in most cases they never will be.

Provision them as Databricks users

Guest-account lifecycle management for hundreds or thousands of external people, across organizations you don’t control, that requires cross-tenant directory federation. Doesn’t scale past a few dozen users.

Filter data in application code

A custom ACL layer in the app that decides what each external user can see. Fragile by construction — one bug exposes another customer’s data, and there’s no independent enforcement layer behind it.

What’s needed: the external IdP handles authentication, Databricks handles authorization and enforcement, and the human never sees a second login screen or a Databricks credential.

What it is · The mechanic

Two legs. One identity. Don’t conflate them.

Leg 1 — Login

Standard OAuth to the external IdP

The user authenticates to their own IdP via authorization_code + PKCE. The app receives an IdP-signed JWT. This is ordinary SSO. Databricks is not involved yet — and the human’s IdP token never leaves the browser or the app’s trust boundary.

Leg 2 — Token exchange

RFC 8693, server-side, at /oidc/v1/token

The app (or a backend it trusts) sends the IdP JWT to Databricks using grant type token-exchange. Databricks validates the JWT against a federation policy (issuer, audience, subject) and returns a Databricks token. This is where per-user identity enters Databricks.

Leg 1 alone never reaches Databricks — sending the raw IdP JWT to Genie Agents or a SQL endpoint fails. Leg 2 is mandatory, and it is the only step Databricks ever sees.
What it is · End to end

Login to governed data access, in one chain.

External Userbrowser / client app
→
External IdPAuth0 · Okta · Entra · any OIDC
→
App / Backendtoken broker, holds no user secrets
↓
RFC 8693 exchange — IdP JWT → Databricks token
/oidc/v1/tokenFederation policy: issuer + audience + subject
↓
Genie Agents · SQL · MCP toolcalled with the exchanged token
↓

Unity Catalog enforces at query time — row filters, column masks, connection grants — then audit records the external user’s identity from the verified JWT claims.

What it is · The decision

Same mechanic, two ways to anchor identity. Pick by whether users can ever be Databricks users.

Per-user OBORole-based service principal
Identity modelEach external user is provisioned as a Databricks identity (SCIM)N users map to O(roles) SPs — no Databricks provisioning
UC evaluatescurrent_user() — the real personis_member() — the SP’s group membership
Scales toHundreds of provisioned usersThousands of external users, unlimited — scales with roles, not people
Individual attributionNative — current_user() is the emailVia JWT-tagged audit table, not current_user()
Best fitKnown users you can SCIM-provision, need native per-user row filtersPartners, auditors, consultants — never becoming Databricks identities

Do not mix the two rules. Row filters written with current_user() only work when the caller genuinely is that user. Row filters written with is_member() only work when the caller is a role-scoped SP. Using the wrong function for the pattern in place fails silently — same result for every user, no error.

What it is · Token anatomy

What’s inside the token at each stage.

Claims below are Auth0-shaped, for illustration. The exchange itself is IdP-agnostic — Okta, Entra, or any OIDC-compliant provider slots into the same three federation-policy fields. Only the claim names and formats shift per provider (see Gotchas).

1. IdP JWT

{
  "iss": "https://idp/",
  "aud": "https://workspace",
  "sub": "{client_id}@clients",
  "groups": ["sales-west"],
  "email": "sarah@partner.com"
}

Signed RS256/ES256, verifiable via the IdP’s JWKS. HS256 blocks this pattern entirely — no shared-secret verification path.

iss → issuer · aud → audience · sub → subject — the same three federation-policy fields as slide 12, whichever IdP is on the other end.

2. Databricks token

{
  "access_token": "eyJ...",
  "expires_in": 3600
}
// opaque; scoped to the
// user or role-SP

Not decodable by the caller. Encodes either the user’s identity (OBO) or the role-SP’s identity, depending on which pattern is in play.

3. Governance context

-- fires automatically,
-- no app code needed
WHERE current_user() = owner
-- or
WHERE is_member('west')

Row filters and column masks fire at the SQL engine, per token. Can’t be bypassed by the application, the IdP, or the query.

Why it matters · Scale

Roles, not people, drive how many identities you manage.

0
Databricks users provisioned for external access, in the role-SP pattern
N → O(roles)
external users to service principals — thousands of partner reps, a handful of SPs

What this replaces

Provisioning a guest account per external person, syncing directories across organizations you don’t control, and managing the lifecycle of every one of those accounts as people join and leave partner firms. None of that is required — identity is anchored in the role, not the individual account.

Why it matters · Security

No second login. No Databricks secret in the external app. Revocable in one statement.

What the user experiences

Standard SSO to their own IdP — nothing else. The token exchange happens entirely server-side, in under 100ms. No separate Databricks credential is ever issued to or held by the end user.

What security gets

Access is revocable with a single REVOKE or by removing the SP from a workspace group — instant, no redeploy. The federation policy is the only thing that trusts the IdP; everything downstream (UC grants, row filters) is completely IdP-agnostic.

Swap the IdP by updating three fields — issuer, audience, subject — on the federation policy. No application code changes.

Why it matters · Compliance

The external identity is preserved end to end, in both patterns.

Silent vs. loud failures — know which you’re looking at

Auth failures (401/403) are loud and visible. UC governance enforcement is silent by design: 0 rows returned or a masked NULL column is governance working correctly, not a bug.

Correlatable audit, regardless of pattern

Whether identity is anchored via current_user() or preserved via JWT-tagged audit records, the external user’s email, role, and action are recoverable — joinable to system.access.audit and MLflow traces.

“Show me what this external user touched last week” is answerable from one audit trail, not a custom application log that may or may not have captured it.

How · The system

Five actors. Twelve prerequisites, verified before you write code.

Five actors, every deployment

Human → SPA → Backend → Databricks → MCP/Genie Agents
            ↑         ↑
          IdP (login)  IdP (M2M token)

IdP, browser app (SPA), backend server (holds secrets, brokers the exchange), Databricks (token endpoint + UC), and the tool server (SQL/Genie Agents/MCP) that runs the governed query.

12 prerequisites, three groups

IdP side (5)SPA app, M2M app, API resource server, claims-injection hook, test users with role metadata
Databricks side (4)Service principals (one per role), federation policy per SP, SP group memberships, warehouse access
Backend side (3)M2M client_id, M2M client_secret (secret store only), role-to-SP mapping table
How · Configuration

The federation policy, and the exchange call.

Federation policy (per SP or account-level)
POST /accounts/{account_id}/servicePrincipals
     /{sp_id}/federationPolicies

{
  "oidc_policy": {
    "issuer":    "https://{idp}/",
    "audiences": ["https://{workspace}"],
    "subject":   "{jwt-sub-claim}"
  }
}
Token exchange (RFC 8693)
POST /oidc/v1/token

grant_type=urn:ietf:params:oauth:
  grant-type:token-exchange
subject_token={idp_jwt}
subject_token_type=
  urn:ietf:params:oauth:token-type:jwt
scope=all-apis
# client_id present -> role-SP token
# client_id omitted -> per-user token

Get the exact values from a real, decoded JWT — never guess them. Trailing slashes on the issuer, @clients suffixes, and GUID-vs-name subject formats all vary by IdP and all cause silent mismatches.

How · The gotcha that matters most

Use the row-filter function that matches your pattern — or it fails silently.

Per-user OBO pattern
RETURN IF(current_user() IN
  (SELECT email FROM approved),
  val, NULL);

Works because the caller genuinely is that user. Using is_member() here evaluates the execution service’s identity, not the human’s — every OBO user sees the same result.

Role-based SP pattern
WHERE is_member('sales_west')
  AND region = 'WEST'

Works because the token is scoped to a role-SP with real group membership. Using current_user() here just returns the SP’s application_id UUID — not useful for per-person filtering.

UC enforcement never errors when the wrong function is used — it just returns the wrong rows, correctly, for the identity it was actually given. Test with two different personas before trusting either pattern in production.
Gotchas

What breaks quietly if you skip the fine print.

IssueDetail
HS256-signed IdP tokensShared-secret signing can’t be validated by Databricks without the secret. Token exchange is unavailable — fall back to SSO login for that IdP.
Subject claim format varies by IdPAuth0: {client_id}@clients. Okta: {client_id}, no suffix. Entra: the app’s Object ID (a GUID), not the Application ID. Decode a real token before writing the policy.
Trailing slash on issuerMust match the JWT’s iss claim exactly. Most IdPs include it; some don’t. A mismatch here is the single most common cause of exchange failures.
Including client_id changes the outcomePresent → token is scoped to that service principal. Omitted → token is scoped to the calling user. Pick deliberately, not by accident.
5-issuer account limitDatabricks allows up to five federated token issuers per account. More IdPs require consolidation or the SSO fallback for the extras.
Wrong row-filter function for the patternSee previous slide — the most consequential mistake on this list.
Go/no-go checklist

Complete this before writing any code.

IdP signs with RS256 or ES256

Confirmed with the IdP team, along with the JWKS URL. This alone determines whether the pattern is possible.

Federation policy fields decoded from a real JWT

Issuer, audience, subject — copied exactly, never guessed.

Row-filter function matches the chosen pattern

current_user() for per-user OBO, is_member() for role-based SPs — not mixed.

Role-to-SP map is the single source of truth

Every IdP role has a mapping entry; unmapped roles are denied, not defaulted.

M2M client secret lives only in the secret store

Never in code, config files, or git history.

Tested with two personas, different grants

Confirmed different data returned before this goes to production.

ONE LOGIN. ONE EXCHANGE. GOVERNED ACCESS.
The IdP authenticates. Databricks authorizes and enforces. The human never sees the difference.
Databricks · Applied AI Governance Series · Cross-IdP Federation