Skip to content

jobs plugin: documented OBO scope jobs.jobs does not exist — no jobs OAuth scope is available for Databricks Apps user authorization #515

Description

@DB-Josh-Killen

Summary

The Jobs plugin documents and ships an OBO (on-behalf-of-user) code path that cannot work on the Databricks Apps platform today. The docs instruct users to add jobs.jobs to user_api_scopes, but no jobs-related OAuth scope exists in the Databricks Apps user-authorization scope list.

Evidence

The authoritative list is the workspace-admin scope picker (Settings → Development → Apps → "Restrict OAuth scopes for apps to selected values"). The scopes offered are:

sql:restricted-query        Restrictive query using Databricks SQL
sql                         Databricks SQL
genie                       Access Databricks Genie
postgres                    Postgres Database
model-serving               Access Databricks Model Serving
catalog.catalogs:read       Read Unity Catalog catalogs
catalog.schemas:read        Read Unity Catalog schemas
catalog.tables:read         Read Unity Catalog tables
catalog.connections         Manage Unity Catalog connections ...
files                       File Management
ai-gateway                  Access Databricks AI Gateway
vector-search               Access Databricks Vector Search
workspace.workspace         Access Databricks Workspace folder
mcp.external                Access External MCP Server
mcp.functions               Access Managed UC Function MCP Server

No jobs, no jobs.jobs, no equivalent. (The picker is scrollable, so this may not be the complete list — but there is no jobs entry among the visible scopes.)

This is not a workspace-allowlist restriction. The workspace tested has the allowlist fully open:

$ databricks workspace-settings-v2 get-public-workspace-setting allowedAppsUserApiScopes -o json
{
  "effective_allowed_apps_user_api_scopes": {
    "allowed_scopes": [ "*" ]
  },
  "name": "allowedAppsUserApiScopes"
}

With * (all supported scopes) allowed, a jobs scope still isn't offered — so it isn't a supported scope at the platform level.

Affected code and docs

This is live on the published documentation site: https://developers.databricks.com/docs/appkit/v0/plugins/jobs

The page advertises it in the Key features list at the top:

  • Multi-job support with named job keys
  • Auto-discovery of jobs from environment variables
  • Run-and-wait with SSE streaming status updates
  • Parameter validation with Zod schemas
  • Task-type-aware parameter mapping (notebook, python_wheel, sql, etc.)
  • Optional on-behalf-of (OBO) user execution via .asUser(req)

And again in the Execution context section, which is the source of the jobs.jobs instruction:

The Jobs UI attributes runs to the app's service principal rather than the human user. For user-level attribution or to apply permission checks based on the user's own grants, explicitly enable OBO execution through .asUser(req) in custom handlers

Source: docs/docs/plugins/jobs.md:128-137 — documents a configuration that cannot be satisfied:

// Opt-in: runs as the logged-in user (requires `jobs.jobs` in
// `databricks.yml` user_api_scopes AND the user's own CAN_MANAGE_RUN grant)
const result = await AppKit.jobs("etl").asUser(req).runNow({ startDate: "2025-01-01" });

packages/appkit/src/plugins/jobs/plugin.ts:742-745exports() attaches asUser to every JobHandle:

asUser: (req: IAppRequest) => {
  const userPlugin = this.asUser(req) as JobsPlugin;
  return userPlugin.createJobAPI(jobKey);
},

packages/appkit/src/plugins/jobs/types.ts:71-74 — the public type advertises it:

/**
 * Job handle returned by `appkit.jobs("etl")`.
 * Supports OBO access via `.asUser(req)`.
 */
export type JobHandle = JobAPI & {
  asUser: (req: IAppRequest) => JobAPI;
};

The mechanism itself is sound — Plugin.asUser() (packages/appkit/src/plugin/plugin.ts:431) reads x-forwarded-access-token and builds a user-scoped WorkspaceClient via ServiceContext.createUserContext(), which createJobAPI then threads into the Jobs connector. The problem is purely that the forwarded OBO token can never carry a jobs scope, so workspace_client.jobs.runNow() under that token will be rejected by the platform.

Worth noting the failure mode is unhelpful: because there is no scope to declare, the deploy succeeds and the failure surfaces only at request time when a user actually clicks the button.

Environment

  • @databricks/appkit 0.55.0 (main @ 14ff6e1)
  • Databricks CLI v1.7.0
  • Workspace: serverless-stable (AWS), Apps user authorization enabled, scope allowlist *

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions