Skip to content

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.