Skip to content

feat(kernel): route Azure Entra auth (azure-sp-m2m + azure-oauth) through the auth bridge - #919

Merged
eric-wang-1990 merged 18 commits into
mainfrom
eric-wang/kernel-azure-oauth
Aug 22, 2026
Merged

eric-wang-1990 merged 18 commits into
mainfrom
eric-wang/kernel-azure-oauth

Conversation

@eric-wang-1990

@eric-wang-1990 eric-wang-1990 commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

What

Teaches the use_kernel=True auth bridge to route both Azure Entra auth flows to the kernel (databricks/databricks-sql-kernel#263):

  • Azure service-principal M2M (auth_type="azure-sp-m2m") — forward the SP credentials to the kernel, which owns Azure resolution.
  • Azure AD U2M (auth_type="azure-oauth") — route to the kernel's OAuth U2M flow, identically to databricks-oauth.

How

src/databricks/sql/backend/kernel/auth_bridge.py:

  • azure-sp-m2m → kernel azure-sp-m2m — forward azure_client_id / azure_client_secret (+ optional azure_tenant_id, azure_workspace_resource_id). The kernel builds the Entra v2.0 token endpoint and the {effective_app_id}/.default scope, and auto-discovers the tenant from the workspace's /aad/auth redirect when azure_tenant_id is omitted (Thrift parity). The Authorization bearer (data token) alone authenticates a workspace-member SP; when azure_workspace_resource_id is set, the kernel also sends the Azure SP management token (X-Databricks-Azure-SP-Management-Token) + X-Databricks-Azure-Workspace-Resource-Id header (matching the JDBC driver), so an SP with an Azure RBAC role but no workspace membership authenticates. PECOBLR-4141.
  • azure-oauth → kernel oauth-u2m — routed through the shared U2M branch, exactly like databricks-oauth. The kernel runs the in-house workspace-federated browser flow, which Azure Databricks workspaces support (the workspace federates login to Entra). It forwards the connector's databricks-sql-python app bundle, not Thrift's Azure app (96eecda7 / redirect port 8030): that app is registered for Thrift's direct-Entra flow (login.microsoftonline.com config + {tenant}/user_impersonation scopes), which the kernel does not perform. So on the kernel path azure-oauth and databricks-oauth are the same flow. The U2M-vs-M2M ambiguity guard now also covers azure-oauth, so azure-oauth + oauth_client_secret is rejected as ambiguous rather than silently falling through to M2M. PECOBLR-4120.
  • src/databricks/sql/session.py threads the azure_* connection kwargs into kernel_auth_options (the kernel owns Azure resolution, so these raw kwargs are the only source).

The binding is thin — it does not construct endpoints or scopes; the kernel does.

Tests

  • TestKernelAzureSpM2M: forwards creds; tenant optional (kernel auto-discovers); azure_workspace_resource_id forwarded; required-creds error; federation client id.
  • TestKernelOAuthU2M: azure-oauth maps to oauth-u2m with the databricks-sql-python bundle (identical to databricks-oauth); custom client_id/port/scopes honored; the ambiguity guard (U2M auth_type + oauth_client_secret) is parametrized over both databricks-oauth and azure-oauth.
  • session-threading test asserts the azure_* kwargs reach auth_options.

Bridge + session + auth unit tests pass; black clean. (test_kernel_client.py skips without the kernel wheel, as before.)

E2E verification (live Azure workspace)

Ran the full connector path (sql.connect(use_kernel=True, ...)) against a live Azure Databricks workspace, kernel wheel built from databricks/databricks-sql-kernel#263. All three Azure kernel-path flows round-trip SELECT 1:

  • Azure U2M (auth_type="azure-oauth") — full interactive browser authorization-code flow (cache cleared to force it). Log confirms the design: authorize URL is the workspace's own OIDC endpoint ({host}/oidc/v1/authorize), client_id=databricks-sql-python, redirect_uri=http://localhost:8020, scope=sql offline_access — i.e. the in-house workspace-federated flow, not Thrift's direct-Entra app (96eecda7 / port 8030 / user_impersonation). Authenticated interactively and returned SELECT 1. ✅
  • Azure SP M2M, data token (auth_type="azure-sp-m2m", no azure_workspace_resource_id) — workspace-member SP, Row(one=1, who='<sp-app-id>'). ✅
  • Azure SP M2M, management token (azure-sp-m2m + azure_workspace_resource_id) — the kernel mints the ARM management token via the Entra v1.0 resource= grant (aud https://management.core.windows.net/, trailing slash) and sends the X-Databricks-Azure-SP-Management-Token + -Workspace-Resource-Id header pair; round-trip succeeds. ✅

Negative controls confirm the pipeline is real, not a no-op: a mismatched SP secret surfaces Entra AADSTS7000215, and a service principal with no workspace access returns HTTP 403 User not authorized (a real control-plane authz decision, not a bridge error).

Note: this required a kernel wheel with the azure-sp-m2m pyo3 surface — the currently-published databricks-sql-kernel 0.2.0 wheel lacks it (Session.__new__() got an unexpected keyword argument 'azure_client_id'), so merging this depends on a kernel release that includes #263. (Azure U2M works on the old wheel — oauth-u2m predates it.) Not yet covered: the RBAC-only authorization case (a non-member SP authorized purely by an Azure role via the management token), which needs an Azure role assignment still pending.

Related

  • Kernel side: databricks/databricks-sql-kernel#263.
  • JIRA: PECOBLR-4141 (azure-sp-m2m), PECOBLR-4120 (azure-oauth → kernel OAuth U2M).

This pull request and its description were written by Isaac.

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-assisted engineer-bot Maintainer-applied gate: triggers engineer-bot (bug-fix on issue / take-over on PR).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants