Databricks Agent Authentication: Declared Resources, On-Behalf-of-User and Secrets
Databricks GenAI Engineer · Deep dive

Databricks Agent Authentication: Declared Resources, On-Behalf-of-User and Secrets

A Databricks agent deployed on Model Serving reaches other resources in one of three ways: automatic authentication passthrough, where a hidden service principal uses the deployer's checked access to resources declared at log time; on-behalf-of-user authentication, where it acts as the person querying it; or manual authentication with credentials kept in secrets.

Last updated November 2026.

What this deep dive adds

Chapter 36 of the course covers the idea: a deployed agent reaches only what it declared, through a shared identity or the caller's own, and endpoint permissions are a separate layer. This post adds the detail: who the agent runs as in each mode, every declarable resource with its required privilege, complete code for all three modes, and the endpoint permission levels.

The objective is "Control access to resources from model serving endpoints", in Section 4 (Assembling and Deploying Applications) of the 18 Mar 2026 guide for the Databricks Certified Generative AI Engineer Associate exam. The docs now steer new agents to Databricks Apps; everything below is the Model Serving version the objective names.

Who the agent runs as

The downstream resource checks the agent's identity, not the caller's, and that identity depends on the mode:

Who a Databricks agent runs as in each authentication mode With automatic passthrough, any caller with CAN QUERY reaches resources as a hidden service principal created per model version, so every caller gets the same reach. With on-behalf-of-user, Ana's downscoped token is used, so the table sees Ana and she reaches only her own grants. With manual authentication, the agent uses a service principal whose credentials you store in secrets, and you create, grant and rotate that credential. CALLER AGENT RUNS AS RESOURCE SEES Automatic passthrough Ana or BenCAN QUERY Hidden SPper version Index seesthe SP Every caller gets the deployer-checked reach On-behalf-of-user AnaCAN QUERY Ana's tokendownscoped Table seesAna Each caller reaches only their own grants Manual authentication Ana or BenCAN QUERY Your SPfrom secrets API seesyour SP You create, grant and rotate the credential
Passthrough and manual give every caller one shared reach; on-behalf-of-user is the only mode where the resource sees the person asking.

Under passthrough, Databricks first verifies that the endpoint creator can access every declared resource, then creates a service principal for that agent model version, grants it read access to those resources, and injects short-lived M2M OAuth credentials that it rotates. That service principal does not appear in API or UI listings and is deleted when the model version leaves the endpoint, so there is nothing to grant by hand: a missing permission is fixed by the declaration.

Every resource you can declare

These are the resource types the docs list for automatic passthrough, with the mlflow.models.resources class, its argument, and the permission the endpoint creator must hold at deploy time:

Resource and classDeployer needs
Vector Search index
DatabricksVectorSearchIndex(index_name=)
Can Use (MLflow 2.13.1+)
Model Serving endpoint
DatabricksServingEndpoint(endpoint_name=)
Can Query (2.13.1+)
SQL warehouse
DatabricksSQLWarehouse(warehouse_id=)
Use Endpoint (2.16.1+)
Unity Catalog function
DatabricksFunction(function_name=)
EXECUTE (2.16.1+)
Genie Space
DatabricksGenieSpace(genie_space_id=)
Can Run (2.17.1+)
Unity Catalog connection
DatabricksUCConnection(connection_name=)
Use Connection (2.17.1+)
Unity Catalog table
DatabricksTable(table_name=)
SELECT (2.18.0+)
Lakebase
DatabricksLakebase(database_instance_name=)
databricks_superuser (3.3.2+)

Sourced from the Databricks documentation pages "Authentication for agents (Model Serving)" and "Access control lists" (serving endpoint ACLs), checked November 2026. Unity Catalog resources also need USE SCHEMA and USE CATALOG on their parents. The docs also show DatabricksApp(app_name=) in the logging example, for a custom MCP server hosted on a Databricks App, but list no permission for it.

Declare downstream resources too: a Genie Space needs its tables, warehouses and functions alongside it. For workspace MCP endpoints, the get_databricks_resources() method of DatabricksMCPClient, in the databricks-mcp package, lists everything to declare.

Automatic passthrough: the logging call

import mlflow
from mlflow.models.resources import (
    DatabricksVectorSearchIndex, DatabricksServingEndpoint,
    DatabricksFunction, DatabricksSQLWarehouse, DatabricksGenieSpace,
    DatabricksTable,
)

with mlflow.start_run():
    logged = mlflow.pyfunc.log_model(
        name="agent",
        python_model="agent.py",
        input_example=input_example,
        resources=[
            DatabricksVectorSearchIndex(index_name="prod.support.docs_index"),
            DatabricksServingEndpoint(endpoint_name="databricks-bge-large-en"),
            DatabricksServingEndpoint(endpoint_name="my-llm-endpoint"),
            DatabricksFunction(function_name="prod.support.lookup_order"),
            # The Genie Space, plus what it depends on
            DatabricksGenieSpace(genie_space_id="<genie_space_id>"),
            DatabricksSQLWarehouse(warehouse_id="<warehouse_id>"),
            DatabricksTable(table_name="prod.sales.orders"),
        ],
    )

The resources list is stored with the logged model. When the registered version is deployed, the permission check runs against the deployer and the hidden service principal is granted exactly this list. Anything the code touches that is missing here fails at query time, not at deploy time.

On-behalf-of-user: clients, then scopes

On-behalf-of-user authentication is in Public Preview, needs MLflow 2.22.1 or above, and is off until a workspace admin enables it. On Model Serving it covers Vector Search indexes, serving endpoints, SQL warehouses, Unity Catalog connections, tables (through SQL statement execution) and functions, Genie Spaces and MCP servers, but not volumes.

from databricks.sdk import WorkspaceClient
from databricks_ai_bridge import ModelServingUserCredentials
from databricks.vector_search.client import VectorSearchClient
from databricks.vector_search.utils import CredentialStrategy
from mlflow.pyfunc import ResponsesAgent

class SupportAgent(ResponsesAgent):
    def predict(self, request):
        # Built per request: the caller is only known now
        user_client = WorkspaceClient(
            credentials_strategy=ModelServingUserCredentials())
        user_vsc = VectorSearchClient(
            credential_strategy=CredentialStrategy.MODEL_SERVING_USER_CREDENTIALS)
        # Runs as the agent's own identity, not the caller
        system_client = WorkspaceClient()
        try:
            index = user_vsc.get_index(endpoint_name="vs-endpoint",
                                       index_name="prod.support.docs_index")
        except Exception:
            index = None  # caller lacks access: skip the tool, do not crash
        ...

Every user-scoped client is created inside predict (or predict_stream), never in __init__ or at import time: the user's identity only exists once a request arrives, and building the client per request keeps invocations isolated from each other. The try/except lets a user without access to one resource still use the rest; the plain WorkspaceClient() runs as the agent.

import mlflow
from mlflow.models.auth_policy import AuthPolicy, SystemAuthPolicy, UserAuthPolicy
from mlflow.models.resources import DatabricksServingEndpoint

system_policy = SystemAuthPolicy(
    resources=[DatabricksServingEndpoint(endpoint_name="my-llm-endpoint")])
user_policy = UserAuthPolicy(api_scopes=["model-serving", "ai-search"])

with mlflow.start_run():
    mlflow.pyfunc.log_model(
        name="agent",
        python_model="agent.py",
        auth_policy=AuthPolicy(system_auth_policy=system_policy,
                               user_auth_policy=user_policy),
    )

For on-behalf-of-user, the docs pass an auth_policy instead of a bare resources list: the system policy holds resources reached with the agent's own credentials, the user policy holds the REST API scopes the caller's token is downscoped to. On Model Serving, only scopes matching the supported resources are allowed. The docs flag one risk: the model-serving scope lets the agent call an endpoint as the user, and that endpoint may reach APIs beyond the agent's own scopes.

Once the caller's identity reaches the data, Unity Catalog's own per-user controls apply to it, which is how row filters and column masks stay in force through an agent.

Manual authentication with secrets

Manual authentication is for a resource passthrough does not cover, credentials other than the deployer's, APIs outside Databricks, or the prompt registry. The docs recommend OAuth for a service principal you create and grant yourself:

import os
from databricks.sdk import WorkspaceClient
from databricks import agents

scope = "agent-secrets"
os.environ["DATABRICKS_HOST"] = "https://<workspace-url>"
os.environ["DATABRICKS_CLIENT_ID"] = dbutils.secrets.get(scope=scope, key="sp-client-id")
os.environ["DATABRICKS_CLIENT_SECRET"] = dbutils.secrets.get(scope=scope, key="sp-client-secret")
w = WorkspaceClient(host=os.environ["DATABRICKS_HOST"],
                    client_id=os.environ["DATABRICKS_CLIENT_ID"],
                    client_secret=os.environ["DATABRICKS_CLIENT_SECRET"])

agents.deploy(UC_MODEL_NAME, version,
    environment_vars={
        "DATABRICKS_HOST": "https://<workspace-url>",
        "DATABRICKS_CLIENT_ID": "{{secrets/agent-secrets/sp-client-id}}",
        "DATABRICKS_CLIENT_SECRET": "{{secrets/agent-secrets/sp-client-secret}}",
    })

The agent code builds its client from the three environment variables, reading the client ID and secret from a secret scope as the docs show. agents.deploy() passes the endpoint the same variables as {{secrets/scope/key}} references, so the secret never appears in plain text in the endpoint config. A PAT works the same way with DATABRICKS_TOKEN, preferably for a service principal. For the prompt registry, that principal needs CREATE FUNCTION, EXECUTE and MANAGE on the prompts schema.

The trap: overriding these authentication environment variables disables automatic passthrough for every other resource the agent depends on. Adding one external API key can break the index that worked yesterday.

Who can call the endpoint

Two checks on every request to an agent endpoint Ana sends a request. Gate one is the endpoint ACL: she needs CAN QUERY or CAN MANAGE, and CAN VIEW alone cannot query. The agent code then runs. Gate two is the agent's identity, which is the passthrough service principal, Ana under on-behalf-of-user, or your own service principal, and it needs the privilege on the resource. Only then does the request reach the index, table, warehouse or Genie Space. TWO CHECKS ON EVERY REQUEST Ana sends a request to the endpoint GATE 1: ENDPOINT ACL CAN QUERY or CAN MANAGE CAN VIEW can get and list, not query The agent code runs GATE 2: THE AGENT'S IDENTITY Passthrough SP, Ana (OBO) or your SP needs the privilege on the resource Index, table, warehouse, Genie Space
The endpoint ACL decides who may ask; the authentication mode decides whose access answers.

Serving endpoint ACLs have three levels. CAN VIEW gets and lists the endpoint. CAN QUERY adds sending requests. CAN MANAGE adds updating the config, deleting the endpoint and changing its permissions. The levels stack, so a manager can also query. Under passthrough, granting CAN QUERY shares the deployer-checked reach; under on-behalf-of-user, each caller still needs their own data access.

Which mode when

SituationMode
Per-user access or user-attributed auditing is requiredOn-behalf-of-user
Shared resources, all of a supported typeAutomatic passthrough (the docs' recommendation)
An unsupported resource, an external API or the prompt registryManual, OAuth service principal in secrets
OBO needed for a resource Model Serving does not support, such as volumesDeploy on Databricks Apps

From the docs' decision flowchart. Modes can be mixed per resource within one agent.

What this means for the exam

Exam tip: "works in the notebook, auth error on the endpoint" points to a resource missing from resources=, fixed by re-logging and redeploying. "Each user sees only their own rows" points to on-behalf-of-user. "Who may invoke the endpoint" is an endpoint ACL question, never a resource question. A shared admin token is always the wrong answer.

How the agent itself is authored, traced and deployed with agents.deploy() is in the Agent Framework and Agent Bricks post.

FAQ

Who does a Databricks agent run as on Model Serving?

With automatic authentication passthrough, the agent runs as a hidden service principal created for its model version, with access to the resources declared at log time. With on-behalf-of-user authentication it acts as the person querying it, and with manual authentication it uses the credentials you supply as environment variables.

Why does my agent work in a notebook but fail on the serving endpoint?

In the notebook the agent runs as you; deployed, it reaches only the resources declared in log_model's resources parameter. Add the missing index, function or endpoint to resources, log a new version and redeploy.

Is on-behalf-of-user authentication generally available for Databricks agents?

No. On-behalf-of-user authentication for agents is in Public Preview, requires MLflow 2.22.1 or above, and is disabled until a workspace admin enables it.

What permission does a user need to query a Databricks serving endpoint?

CAN QUERY on the endpoint, or CAN MANAGE, which includes it. CAN VIEW only gets and lists the endpoint.

Where should a Databricks agent create its on-behalf-of-user clients?

Inside predict or predict_stream, not in __init__ or at import time, because the caller's identity only exists when a request arrives.

Back to the chapter

Controlling access to resources from a serving endpoint is Chapter 36 of the Databricks Generative AI Engineer Associate course, in Unit 4, Assembling and Deploying Applications, with declared resources, the two identities and endpoint ACLs built up card by card and exam-style checks.

Open Chapter 36 →