identity
Which Databricks identity a request’s workspace calls run as.
AppKit gives a plugin two identities: the ambient SERVICE context (the app’s
own service principal) and a per-request USER context entered with
asUser(req), which authenticates as the caller on-behalf-of (OBO). The
choice is one call, so the whole decision is “do we enter asUser for this
request”.
asUser(req) needs the OBO token the platform front door forwards on
ACCESS_TOKEN_HEADER. Measured against the installed AppKit, its
behavior when that header is absent depends ENTIRELY on NODE_ENV:
NODE_ENV |
no x-forwarded-access-token |
|---|---|
development |
logs a warning, silently runs as the service principal |
| anything else | throws AuthenticationError: Missing user token |
That production throw is correct for an app behind the Databricks front door, where a missing token means something is wrong. It is fatal for an app whose traffic legitimately arrives WITHOUT one:
- a public tunnel (
@dbx-tools/tunnel), where callers authenticate by email OTP and no OBO token exists to forward - the gate can prove WHO the caller is, but it cannot mint a Databricks credential for them; - any reverse proxy, webhook, or bot channel (
POST /api/teams/messages) that authenticates its own way.
Such an app must not run with NODE_ENV=development just to get the fallback:
that flag also relaxes secure cookies, AppKit’s own dev affordances, and
allowUnauthenticated escape hatches. Hence IdentityMode:
"user"- always OBO. Per-user attribution and per-user Genie / Unity Catalog row filters. Correct when every caller is a workspace member."service-principal"- always the app’s own identity. Needs no OBO scopes and works for any caller, at the cost of per-user data scoping."auto"- OBO when the request actually carries a usable OBO token, the service principal otherwise. One deployment then serves BOTH doors correctly: front-door requests keep full per-user scoping, while tunnel / webhook requests degrade to the service principal instead of 500ing.
"auto" decides per REQUEST, not per boot, because a single container serves
both doors at once - the tunnel gate and the platform front door share a port
(see @dbx-tools/tunnel). A boot-time flag would have to be wrong for one
of them.
What the service principal does NOT change is WHO the request belongs to. The caller’s identity still arrives on USER_ID_HEADER / USER_EMAIL_HEADER, so memory threads, cache namespaces, and trace attribution stay per-user. Only the Databricks credential is shared.