Skip to content

Commit 9e6ffe9

Browse files
refactor(kernel): reject azure-oauth on the kernel path; point to databricks-oauth
The kernel dropped its dedicated Azure U2M flow, so `auth_type="azure-oauth"` on `use_kernel=True` is now rejected with a clear pointer to `databricks-oauth` — whose in-house OAuth browser flow works against Azure workspaces (the workspace federates login to Entra). This replaces the thin forward-the-selector branch. `azure-sp-m2m` is unchanged; `azure-oauth` on the Thrift path is unaffected. Rejecting (rather than silently remapping) keeps the user's selected flow explicit. Co-authored-by: Isaac Signed-off-by: eric-wang-1990 <e.wang@databricks.com>
1 parent dbf2b5a commit 9e6ffe9

4 files changed

Lines changed: 41 additions & 62 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# Unreleased
44
- Kernel backend (`use_kernel=True`): OAuth U2M with `auth_type="databricks-oauth"` now forwards the connector's `databricks-sql-python` OAuth-app bundle (`client_id` + `sql offline_access` scopes + redirect port) into the kernel, so a bare U2M connection authenticates as `databricks-sql-python` — parity with the Thrift path — instead of inheriting the kernel's own `databricks-sql-connector` default. A caller-supplied `oauth_client_id` (with its coupled `oauth_redirect_port`) is honored, as is a caller-supplied `oauth_scopes`; absent one, the connector default (`sql offline_access`) is forwarded. Note: the kernel binds a single U2M redirect port, so unlike the Thrift path (which tries the full `8020..8024` range) the kernel path uses only one port and does not fall back to the next port if it is already bound — pass `oauth_redirect_port` (with `oauth_client_id`) to pick a free one on a port collision (PECOBLR-4040)
5-
- Kernel backend (`use_kernel=True`): **Azure Entra (Azure AD) OAuth is now supported.** The kernel is the Azure-aware auth core (it owns the endpoints, scopes, app ids, and tenant discovery); the connector forwards the selector and Azure credentials unchanged, so `connect()` is byte-identical between the Thrift and `use_kernel=True` paths. `auth_type="azure-oauth"` (Azure AD U2M) runs the kernel's browser flow against the workspace v2.0 authorize/token endpoints with the Azure app client id (`96eecda7-…`), redirect port `8030`, and the `{app_id}/user_impersonation offline_access` scope. `auth_type="azure-sp-m2m"` (Azure service principal) forwards `azure_client_id` / `azure_client_secret`; the kernel builds the Entra v2.0 token endpoint and the `{app_id}/.default` scope, and **auto-discovers the tenant** from the workspace's `/aad/auth` redirect when `azure_tenant_id` is omitted (matching Thrift). The service principal authenticates with the Databricks-audience data token, so it must be a workspace member; the kernel path does not yet support the Azure management-token flow (for an RBAC-only SP), so `azure_workspace_resource_id` is ignored with a warning on `use_kernel=True` (it still applies on the Thrift path) (PECOBLR-4141; PECOBLR-4120)
5+
- Kernel backend (`use_kernel=True`): **Azure Entra (Azure AD) service-principal M2M is now supported.** `auth_type="azure-sp-m2m"` forwards `azure_client_id` / `azure_client_secret`; the kernel is the Azure-aware auth core — it builds the Entra v2.0 token endpoint and the `{app_id}/.default` scope, and **auto-discovers the tenant** from the workspace's `/aad/auth` redirect when `azure_tenant_id` is omitted (matching Thrift). The service principal authenticates with the Databricks-audience data token, so it must be a workspace member; the kernel path does not yet support the Azure management-token flow (for an RBAC-only SP), so `azure_workspace_resource_id` is ignored with a warning on `use_kernel=True` (it still applies on the Thrift path). Azure AD **U2M** (`auth_type="azure-oauth"`) is **not** supported on the kernel path — use `auth_type="databricks-oauth"`, whose in-house OAuth browser flow works against Azure workspaces (the workspace federates login to Entra); `azure-oauth` on `use_kernel=True` is rejected with that pointer (PECOBLR-4141; PECOBLR-4120)
66

77
# 4.4.0 (2026-07-22)
88
- Raised the minimum supported Python version to 3.10, dropping the end-of-life 3.8/3.9, to update the lockfile and clear CVE-flagged dependencies in the repo (databricks/databricks-sql-python#798)

CONNECTION_PARAMETERS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ to change without notice.
6969
| Option | Type | Thrift | Kernel | Default Value | Note |
7070
| --------------------------------------------------- | -------------------- | :----: | :----: | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
7171
| `access_token` (PAT) | `str` ||| `None` | Personal Access Token / bearer token. The default auth mode when set; otherwise auth falls back to OAuth. |
72-
| `auth_type` | `str` ||| `None` ⇒ Databricks OAuth | `databricks-oauth` (U2M), `azure-oauth` (Azure AD U2M), or `azure-sp-m2m` (Azure service-principal M2M). All three work on the kernel path; `azure-oauth` / `azure-sp-m2m` kernel support added in #919 (the connector forwards the selector + Azure credentials and the kernel owns Azure resolution). |
72+
| `auth_type` | `str` ||| `None` ⇒ Databricks OAuth | `databricks-oauth` (U2M), `azure-oauth` (Azure AD U2M), or `azure-sp-m2m` (Azure service-principal M2M). On the kernel path: `databricks-oauth` and `azure-sp-m2m` are supported (#919); `azure-oauth` is **not** — use `databricks-oauth` instead (its in-house browser flow works against Azure workspaces), and `azure-oauth` on `use_kernel=True` is rejected with that pointer. All three work on the Thrift path. |
7373
| `oauth_client_id` (U2M) | `str` ||| built-in client id | Custom U2M client id. Forwarded on both; when absent, each path applies its own built-in default. |
7474
| `oauth_redirect_port` (U2M) | `int` ||| `None` | Localhost redirect port for the browser flow. On **both** backends it is only honored when a custom `oauth_client_id` is also supplied — then that single port becomes the redirect URI. With the built-in client id (or when omitted) the connector uses the full registered range 8020–8024 and binds the first free port, so a bare `oauth_redirect_port` has no effect. (Thrift: `auth.py` `oauth_redirect_port_range`; Kernel: same logic, forwarded as `redirect_ports`.) |
7575
| `oauth_client_secret` (OAuth M2M) | `str` ||| `None` | **Kernel-only in practice.** The Thrift auth path never reads `oauth_client_secret`; use `credentials_provider` or an Azure service principal for M2M on Thrift. |

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

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,14 @@
2121
``oauth_client_id`` / ``oauth_redirect_port`` overriding it) is
2222
forwarded to the kernel's ``auth_type='oauth-u2m'`` and the kernel
2323
runs the browser flow itself.
24-
- **Azure Entra (Azure AD)** — both Azure auth types forward the selector
25-
and Azure credentials to the KERNEL, which is the Azure-aware auth core
26-
(it owns the endpoints, scopes, app ids, and tenant discovery). The
27-
binding stays thin — it does not construct endpoints or scopes:
28-
29-
- ``azure-oauth`` (U2M) → forward ``auth_type='azure-oauth'`` (plus any
30-
optional ``oauth_client_id`` / ``oauth_redirect_port`` passthrough). The
31-
kernel pins the workspace v2.0 authorize/token endpoints, the Azure app
32-
client id (``96eecda7-…``), port ``8030``, and the
33-
``{app_id}/user_impersonation offline_access`` scope (PECOBLR-4120).
24+
- **Azure Entra (Azure AD)** — the KERNEL is the Azure-aware auth core (it
25+
owns the endpoints, scopes, app ids, and tenant discovery); the binding
26+
forwards the selector + credentials and does not construct endpoints:
27+
28+
- ``azure-oauth`` (U2M) → **not supported on the kernel path**; rejected with
29+
a pointer to ``databricks-oauth``, whose in-house U2M browser flow works
30+
against Azure workspaces (the workspace federates login to Entra). A
31+
dedicated Azure U2M flow may return later (PECOBLR-4120).
3432
- ``azure-sp-m2m`` (M2M) → forward ``auth_type='azure-sp-m2m'`` with the
3533
Azure service-principal ``azure_client_id`` / ``azure_client_secret``.
3634
The kernel builds the Entra v2.0 token endpoint and the
@@ -170,9 +168,10 @@ def kernel_auth_kwargs(
170168
- a U2M ``auth_type`` (``databricks-oauth``) *and*
171169
``oauth_client_secret`` together.
172170
173-
(The Azure Entra auth types — ``azure-oauth`` and ``azure-sp-m2m`` —
174-
are forwarded to the kernel's Azure-aware flows up front, before these
175-
guards; see the module docstring.)
171+
(The Azure Entra auth types are handled up front, before these guards:
172+
``azure-sp-m2m`` forwards to the kernel's Azure SP flow; ``azure-oauth``
173+
is rejected with a pointer to ``databricks-oauth``. See the module
174+
docstring.)
176175
1. **OAuth M2M** — ``oauth_client_id`` + ``oauth_client_secret``
177176
both present → forward raw creds to the kernel's ``oauth-m2m``.
178177
2. **PAT** — the built provider is (or wraps) an
@@ -211,24 +210,20 @@ def kernel_auth_kwargs(
211210
# creds in azure_* kwargs, not oauth_client_id/secret, so it would
212211
# otherwise fall through to the final "unsupported" error).
213212

214-
# azure-oauth (Azure AD U2M): forward the selector; the KERNEL owns Azure
215-
# resolution (it is the auth core). The kernel pins the workspace v2.0
216-
# authorize/token endpoints (`{host}/oidc/oauth2/v2.0/{authorize,token}` —
217-
# NOT the discovered `/oidc/v1/authorize`, which the workspace redirects to
218-
# a malformed Entra URL), the Azure app client id, port 8030, and the
219-
# `{app_id}/user_impersonation offline_access` scope. So this binding does
220-
# NOT construct endpoints/scopes — it just passes `auth_type='azure-oauth'`
221-
# plus any optional client_id / redirect_port passthrough. PECOBLR-4120.
213+
# azure-oauth (Azure AD U2M) is NOT supported on the kernel path. The
214+
# in-house `databricks-oauth` browser flow works against Azure workspaces
215+
# (the workspace federates the login to Entra), so it is the U2M path on the
216+
# kernel — reject `azure-oauth` with a clear pointer rather than silently
217+
# changing the user's selected flow. (A dedicated Azure U2M flow may return
218+
# later; for now Azure U2M = `databricks-oauth`.)
222219
if auth_type == "azure-oauth":
223-
kwargs = {"auth_type": "azure-oauth"}
224-
if client_id:
225-
kwargs["client_id"] = client_id
226-
redirect_port = opts.get("oauth_redirect_port")
227-
if redirect_port is not None:
228-
kwargs["redirect_ports"] = [_coerce_redirect_port(redirect_port)]
229-
if federation_client_id:
230-
kwargs["identity_federation_client_id"] = federation_client_id
231-
return kwargs
220+
raise NotSupportedError(
221+
"auth_type='azure-oauth' is not supported on use_kernel=True. Use "
222+
"auth_type='databricks-oauth' instead — the in-house OAuth U2M "
223+
"browser flow works against Azure Databricks workspaces (the "
224+
"workspace federates the login to Microsoft Entra). Or use the "
225+
"Thrift backend (default) for the dedicated Azure AD U2M flow."
226+
)
232227

233228
# azure-sp-m2m (Azure service principal, client-credentials): forward the
234229
# selector + Azure SP credentials; the KERNEL owns Azure resolution (it is

tests/unit/test_kernel_auth_bridge.py

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,10 @@ class TestKernelOAuthU2M:
258258
may override ``oauth_scopes``; absent one, ``PYSQL_OAUTH_SCOPES`` is
259259
forwarded as the default.
260260
261-
``azure-oauth`` (Azure AD U2M) routes here too — see
262-
``test_azure_oauth_routes_to_kernel_u2m`` (PECOBLR-4120)."""
261+
``azure-oauth`` (Azure AD U2M) is NOT supported on the kernel path and is
262+
rejected with a pointer to ``databricks-oauth`` — see
263+
``test_azure_oauth_rejected_with_pointer_to_databricks_oauth``
264+
(PECOBLR-4120)."""
263265

264266
def test_bare_databricks_oauth_forwards_full_python_bundle(self):
265267
# No overrides → forward the databricks-sql-python bundle in full
@@ -277,34 +279,16 @@ def test_bare_databricks_oauth_forwards_full_python_bundle(self):
277279
"oauth_scopes": list(PYSQL_OAUTH_SCOPES),
278280
}
279281

280-
def test_azure_oauth_forwards_selector_kernel_owns_resolution(self):
281-
# azure-oauth (Azure AD U2M): the bridge forwards ONLY the selector.
282-
# The kernel owns Azure resolution — it pins the workspace v2.0
283-
# authorize/token endpoints, the Azure client id, port 8030, and the
284-
# {app_id}/user_impersonation scope. So the bridge must NOT construct
285-
# client_id / redirect_ports / oauth_scopes here. PECOBLR-4120.
286-
kwargs = kernel_auth_kwargs(
287-
_FakeOAuthProvider(),
288-
{"auth_type": "azure-oauth"},
289-
)
290-
assert kwargs == {"auth_type": "azure-oauth"}
291-
292-
def test_azure_oauth_honors_custom_client_id_and_port(self):
293-
# A caller override still passes through (client_id + its coupled port),
294-
# but no scopes/endpoints are synthesised by the bridge.
295-
kwargs = kernel_auth_kwargs(
296-
_FakeOAuthProvider(),
297-
{
298-
"auth_type": "azure-oauth",
299-
"oauth_client_id": "custom-azure-app",
300-
"oauth_redirect_port": 9100,
301-
},
302-
)
303-
assert kwargs == {
304-
"auth_type": "azure-oauth",
305-
"client_id": "custom-azure-app",
306-
"redirect_ports": [9100],
307-
}
282+
def test_azure_oauth_rejected_with_pointer_to_databricks_oauth(self):
283+
# azure-oauth (Azure AD U2M) is not supported on the kernel path. The
284+
# in-house databricks-oauth browser flow works against Azure workspaces,
285+
# so the bridge rejects azure-oauth with a clear pointer rather than
286+
# silently changing the user's selected flow. PECOBLR-4120.
287+
with pytest.raises(NotSupportedError, match="databricks-oauth"):
288+
kernel_auth_kwargs(
289+
_FakeOAuthProvider(),
290+
{"auth_type": "azure-oauth"},
291+
)
308292

309293
def test_u2m_custom_client_id_port_and_scopes_honored(self):
310294
# A caller may override the coupled client_id + redirect port and the

0 commit comments

Comments
 (0)