Skip to content

Commit 01acb39

Browse files
refactor(kernel): thin azure-oauth — kernel owns Azure resolution
The kernel is the auth core now: for azure-oauth the bridge forwards only auth_type='azure-oauth' (+ optional client_id/redirect_port passthrough), and the kernel pins the workspace v2.0 authorize/token endpoints, the Azure app client id, port 8030, and the user_impersonation scope. Drops the connector-side endpoint/scope construction (and the AzureOAuthEndpointCollection / PYSQL_OAUTH_AZURE_* imports) from the kernel path. Live-verified end-to-end against an Azure workspace. azure-sp-m2m still routes to oauth-m2m here pending the kernel's dedicated azure-sp-m2m variant. Co-authored-by: Isaac Signed-off-by: eric-wang-1990 <e.wang@databricks.com>
1 parent c2dec51 commit 01acb39

2 files changed

Lines changed: 28 additions & 47 deletions

File tree

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

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,12 @@
6767
from typing import Any, Dict, Optional
6868

6969
from databricks.sql.auth.auth import (
70-
PYSQL_OAUTH_AZURE_CLIENT_ID,
71-
PYSQL_OAUTH_AZURE_REDIRECT_PORT_RANGE,
7270
PYSQL_OAUTH_CLIENT_ID,
7371
PYSQL_OAUTH_REDIRECT_PORT_RANGE,
7472
PYSQL_OAUTH_SCOPES,
7573
)
7674
from databricks.sql.auth.authenticators import AccessTokenAuthProvider, AuthProvider
7775
from databricks.sql.auth.common import get_effective_azure_login_app_id
78-
from databricks.sql.auth.endpoint import AzureOAuthEndpointCollection
7976
from databricks.sql.auth.token_federation import TokenFederationProvider
8077
from databricks.sql.exc import NotSupportedError, ProgrammingError
8178

@@ -218,28 +215,21 @@ def kernel_auth_kwargs(
218215
# creds in azure_* kwargs, not oauth_client_id/secret, so it would
219216
# otherwise fall through to the final "unsupported" error).
220217

221-
# azure-oauth (Azure AD U2M): forward the Azure app bundle to oauth-u2m.
222-
# The kernel runs the browser flow and discovers endpoints via the
223-
# workspace /oidc redirector (which an Azure workspace redirects to Entra).
224-
# The AAD delegated scope ({app_id}/user_impersonation [+ offline_access])
225-
# is synthesised via AzureOAuthEndpointCollection, which also honors the
226-
# DATABRICKS_AZURE_TENANT_ID app-id override. PECOBLR-4120.
218+
# azure-oauth (Azure AD U2M): forward the selector; the KERNEL owns Azure
219+
# resolution (it is the auth core). The kernel pins the workspace v2.0
220+
# authorize/token endpoints (`{host}/oidc/oauth2/v2.0/{authorize,token}` —
221+
# NOT the discovered `/oidc/v1/authorize`, which the workspace redirects to
222+
# a malformed Entra URL), the Azure app client id, port 8030, and the
223+
# `{app_id}/user_impersonation offline_access` scope. So this binding does
224+
# NOT construct endpoints/scopes — it just passes `auth_type='azure-oauth'`
225+
# plus any optional client_id / redirect_port passthrough. PECOBLR-4120.
227226
if auth_type == "azure-oauth":
228-
redirect_port = opts.get("oauth_redirect_port")
229-
caller_scopes = _normalize_scopes(opts.get("oauth_scopes"))
230-
mapped_scopes = AzureOAuthEndpointCollection().get_scopes_mapping(
231-
caller_scopes if caller_scopes is not None else list(PYSQL_OAUTH_SCOPES)
232-
)
233-
kwargs = {
234-
"auth_type": "oauth-u2m",
235-
"client_id": client_id or PYSQL_OAUTH_AZURE_CLIENT_ID,
236-
"redirect_ports": (
237-
[_coerce_redirect_port(redirect_port)]
238-
if client_id and redirect_port is not None
239-
else list(PYSQL_OAUTH_AZURE_REDIRECT_PORT_RANGE)
240-
),
241-
"oauth_scopes": mapped_scopes,
242-
}
227+
kwargs = {"auth_type": "azure-oauth"}
228+
if client_id:
229+
kwargs["client_id"] = client_id
230+
redirect_port = opts.get("oauth_redirect_port")
231+
if redirect_port is not None:
232+
kwargs["redirect_ports"] = [_coerce_redirect_port(redirect_port)]
243233
if federation_client_id:
244234
kwargs["identity_federation_client_id"] = federation_client_id
245235
return kwargs

tests/unit/test_kernel_auth_bridge.py

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,8 @@
3131
PYSQL_OAUTH_CLIENT_ID,
3232
PYSQL_OAUTH_SCOPES,
3333
PYSQL_OAUTH_REDIRECT_PORT_RANGE,
34-
PYSQL_OAUTH_AZURE_CLIENT_ID,
35-
PYSQL_OAUTH_AZURE_REDIRECT_PORT_RANGE,
3634
)
3735
from databricks.sql.auth.common import get_effective_azure_login_app_id
38-
from databricks.sql.auth.endpoint import AzureOAuthEndpointCollection
3936
from databricks.sql.auth.authenticators import (
4037
AccessTokenAuthProvider,
4138
AuthProvider,
@@ -280,30 +277,21 @@ def test_bare_databricks_oauth_forwards_full_python_bundle(self):
280277
"oauth_scopes": list(PYSQL_OAUTH_SCOPES),
281278
}
282279

283-
def test_azure_oauth_routes_to_kernel_u2m(self):
284-
# azure-oauth (Azure AD U2M) now routes to the kernel's oauth-u2m with
285-
# the Azure app bundle: the Azure client id, its registered port 8030,
286-
# and the AAD delegated scope ({app_id}/user_impersonation +
287-
# offline_access). The kernel discovers endpoints via the workspace
288-
# /oidc redirector (which an Azure workspace redirects to Entra).
289-
# PECOBLR-4120.
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.
290286
kwargs = kernel_auth_kwargs(
291287
_FakeOAuthProvider(),
292288
{"auth_type": "azure-oauth"},
293289
)
294-
assert kwargs == {
295-
"auth_type": "oauth-u2m",
296-
"client_id": PYSQL_OAUTH_AZURE_CLIENT_ID,
297-
"redirect_ports": list(PYSQL_OAUTH_AZURE_REDIRECT_PORT_RANGE),
298-
"oauth_scopes": AzureOAuthEndpointCollection().get_scopes_mapping(
299-
list(PYSQL_OAUTH_SCOPES)
300-
),
301-
}
302-
# Sanity: the mapped scope is the AAD delegated form, not `sql`.
303-
assert any(s.endswith("/user_impersonation") for s in kwargs["oauth_scopes"])
304-
assert "offline_access" in kwargs["oauth_scopes"]
290+
assert kwargs == {"auth_type": "azure-oauth"}
305291

306292
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.
307295
kwargs = kernel_auth_kwargs(
308296
_FakeOAuthProvider(),
309297
{
@@ -312,8 +300,11 @@ def test_azure_oauth_honors_custom_client_id_and_port(self):
312300
"oauth_redirect_port": 9100,
313301
},
314302
)
315-
assert kwargs["client_id"] == "custom-azure-app"
316-
assert kwargs["redirect_ports"] == [9100]
303+
assert kwargs == {
304+
"auth_type": "azure-oauth",
305+
"client_id": "custom-azure-app",
306+
"redirect_ports": [9100],
307+
}
317308

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

0 commit comments

Comments
 (0)