How can AI agents access data using the permissions of the requesting user?
Summary
- Agents access data with the requesting user's permissions through on-behalf-of (OBO) identity: the user's token flows through the agent to every downstream query, so access checks see the real person, not a shared service account.
- Unity Catalog is the single authorization engine. It applies the user's row filters, column masks, and privilege grants at query execution, so an agent can never read data the user cannot.
- Enforcement happens at the SQL engine inside the compute layer, not in application code or a network proxy, so the same rules hold whether the query comes from a Genie Agent, Agent Bricks, an MCP client, a notebook, or a dashboard.
- Unity Gateway is the control plane for AI traffic: it routes every model and tool request, tracks usage, and applies service policies that can allow, deny, or require approval based on who is calling.
- Design detail: write policies on current_user() rather than group-membership checks so identity resolves to the actual caller through the OBO chain.
How can AI agents access data using the permissions of the requesting user?
An AI agent accesses data as the requesting user by carrying that user's identity through every step of the request, a pattern called on-behalf-of (OBO) access. The user's token flows to the agent and on to each query and tool call, so the system authorizes work against the person who asked, not a broad service account. On Databricks, Unity Catalog then enforces that user's row filters, column masks, and privileges at query execution, and Unity Gateway governs the model and tool traffic in between. The result is worth stating plainly: an agent can never read data its user cannot.
What is on-behalf-of (OBO) access?
OBO is an authentication pattern that preserves a human's identity end to end instead of swapping it for the application's own credentials. The calling app passes the user's token to the agent, and the agent presents that same identity to every downstream service. Because the identity stays intact, a function like current_user() in a row-filter policy resolves to the actual person, and access decisions reflect exactly what that person is allowed to see. With a shared service principal, every request looks identical and per-user rules cannot apply.
A subtle but important design point: build policies on current_user() rather than is_member() group checks, so identity resolves to the real caller through the OBO chain.
Key capabilities to evaluate
| Capability | Why it matters |
|---|---|
| End-to-end identity propagation | The requesting user's identity must reach every query and tool, including sub-agents, or per-user rules silently stop applying. |
| Enforcement at the query engine | Access rules that fire inside the compute layer cannot be bypassed by a different API path, prompt, or client. |
| Row filters and column masks | The agent should see fewer rows and masked values automatically, with no custom code in the agent itself. |
| One policy set for people and agents | Reusing the same grants for humans and their agents avoids a parallel, drift-prone permission model. |
| Traffic and tool governance | A control plane over model and tool calls lets you track usage and apply allow, deny, or approval rules per caller. |
| Auditable decisions | Every access and tool call should be logged so you can answer who accessed what, and whether policy was enforced. |
How Databricks approaches user-scoped agent access
Databricks treats agent access as an extension of data governance rather than a separate system. Agents can authenticate with OBO so they run as the calling user, and in multi-agent systems that identity propagates to sub-agents, so downstream checks always see the actual person.
Unity Catalog is the single authorization engine. When an agent queries a table, Unity Catalog evaluates the request against the user's privileges, attribute-based policies, and row filters and column masks before results return. Row filters inject a predicate that limits which records come back; column masks transform or redact sensitive values. Both apply transparently at query time. The key property is where this happens: enforcement fires at the SQL engine inside the compute layer, not in the agent's code or a network proxy. That is why the same boundary holds whether data is reached through a Genie Agent, Agent Bricks, a custom MCP client, a notebook, or a BI dashboard. An application cannot route around it.
Models, functions, MCP servers, and connections are themselves Unity Catalog securables, governed with the same privileges you use for tables and volumes. On top of that, Unity Gateway acts as the front door for AI traffic: it routes every model and MCP request from one control plane, records usage, and applies service policies that can allow, deny, or require approval for a request or response based on the caller and the content. For deeper patterns, see the Databricks writeup on governing AI agents at scale with Unity Catalog.
One caution: if an external gateway placed between the app and Databricks reissues its own token, the user's identity is lost and current_user() resolves to the gateway's service identity, quietly breaking per-user governance.
FAQs
Can an agent see data the requesting user cannot?
No. With OBO, the agent inherits the user's exact permission boundary. Unity Catalog applies the same row filters, column masks, and privilege denials it would apply to that user querying directly, so restricted rows and columns never reach the agent.
Where are permissions enforced?
At the SQL engine, during query execution, inside the Databricks compute layer. Because the check is not in application code or a network hop, an agent cannot bypass it with a different query path or client.
Do agent permissions apply to tools and models too?
Yes. Models, functions, MCP servers, and connections are Unity Catalog securables, and Unity Gateway routes model and tool calls through a control plane that enforces usage tracking and service policies per caller.
What happens in a multi-agent system?
The requesting user's identity propagates from the top-level agent to each sub-agent, so every downstream data and tool call is authorized against the original user rather than an intermediate service account.
Should policies use current_user() or group membership?
Prefer current_user() for user-scoped access. It reflects the real caller through the OBO chain, while a group-membership check may evaluate the execution identity instead of the person who invoked the agent.
The information provided herein is for general informational purposes only and may not reflect the most current product capabilities or configurations.