Skip to content

Commit 39891ac

Browse files
Discovery-driven login with workspace selection for SPOG hosts (#4809)
## Why SPOG (Single Pane of Glass) hosts use one URL for both account-level and workspace-level APIs. The CLI's login flow doesn't call the `.well-known/databricks-config` discovery endpoint, so it can't detect SPOG hosts. This means `account_id` and `workspace_id` don't get populated for SPOG profiles, and users of multi-workspace SPOG accounts have no way to set `workspace_id` through the interactive login flow. ## Changes The CLI now calls discovery during login to detect SPOG hosts and auto-populate fields. Users can also paste browser URLs with query params directly into `auth login`. **Before:** SPOG hosts classified as regular workspace hosts. No account_id/workspace_id discovery. Users must manually edit `.databrickscfg`. **Now:** Discovery runs automatically during login. SPOG fields are populated from `.well-known/databricks-config`. Multi-workspace accounts get an interactive workspace picker. Concrete changes: - **URL query param parsing** in `setHostAndAccountId()`: extracts `?o=` (workspace_id) and `?a=` (account_id) from host URLs, strips query params from host. Explicit flags take precedence over URL params. - **Host metadata discovery**: calls `EnsureResolved()` with a temporary config during login to fetch `.well-known/databricks-config`. Populates account_id/workspace_id if not already set. Best-effort, never blocks login on failure. - **`ToOAuthArgument()` refactored**: routes OAuth flow based on `DiscoveryURL` from `EnsureResolved()` instead of `Experimental_IsUnifiedHost` flag. Account-scoped OIDC endpoints (`/oidc/accounts/`) trigger unified OAuth. Classic workspace hosts are never misrouted. - **Profile matching simplified**: `MatchWorkspaceProfiles()` uses `WorkspaceID != ""` presence. `MatchAccountProfiles()` uses `AccountID != "" && WorkspaceID == ""`. Both still handle legacy `IsUnifiedHost` profiles. - **Post-auth workspace selection**: for SPOG hosts with account_id but no workspace_id, prompts user to select from `Workspaces.List()`. Auto-selects single workspace. Capped at 50 entries. Skippable. Non-interactive mode proceeds without selection. - **Token path updated**: `loadToken()` uses workspace_id for disambiguation when matching multi-workspace SPOG profiles. **Note on discovery in `ToOAuthArgument()`:** This function now calls `EnsureResolved()` on every invocation, which makes an unauthenticated HTTP request to `{host}/.well-known/databricks-config`. This is a deliberate tradeoff. The alternative was to persist additional state (like `discovery_url` or `experimental_is_unified_host`) to `.databrickscfg` so we could route the OAuth flow without a network call. We chose not to do that because we want to move away from persisting host-type markers to profiles and instead derive behavior from the host at runtime. The added latency (~100ms per call) will be addressed by a separate discovery caching PR that caches `.well-known/databricks-config` responses with a 1-hour TTL, making repeated calls essentially free. Backward compatibility: - Existing profiles with `experimental_is_unified_host = true` still work (legacy fallback in `ToOAuthArgument()`) - Classic `accounts.*` login flow unchanged - Regular workspace login unchanged - `--experimental-is-unified-host` flag preserved in re-auth error messages ## Test plan - [x] Table-driven tests for URL query param parsing (all param combinations, explicit flag precedence, invalid URLs) - [x] Discovery tests with mock HTTP server (SPOG host, classic workspace host, discovery failure) - [x] `ToOAuthArgument()` routing tests (SPOG -> unified, classic workspace not misrouted, env var contamination prevented, legacy IsUnifiedHost fallback) - [x] Profile matching tests (all profile types: regular workspace, regular account, SPOG workspace, SPOG account, legacy unified) - [x] Token path profile disambiguation tests (workspace_id matching for multi-workspace SPOG) - [x] `IsAccountsHost()` helper tests - [x] `make checks` passes - [x] All existing auth tests pass --------- Co-authored-by: Andrew Nester <andrew.nester.dev@gmail.com>
1 parent 19f9dbe commit 39891ac

File tree

25 files changed

+801
-110
lines changed

25 files changed

+801
-110
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[DEFAULT]
22
host = [DATABRICKS_URL]
33
serverless_compute_id = auto
4+
workspace_id = [NUMID]
45
auth_type = databricks-cli

acceptance/cmd/auth/login/configure-serverless/output.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ Profile DEFAULT was successfully saved
1212
[DEFAULT]
1313
host = [DATABRICKS_URL]
1414
serverless_compute_id = auto
15+
workspace_id = [NUMID]
1516
auth_type = databricks-cli

acceptance/cmd/auth/login/custom-config-file/out.databrickscfg

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
[DEFAULT]
33

44
[custom-test]
5-
host = [DATABRICKS_URL]
6-
auth_type = databricks-cli
5+
host = [DATABRICKS_URL]
6+
auth_type = databricks-cli
7+
workspace_id = [NUMID]

acceptance/cmd/auth/login/custom-config-file/output.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ OK: Default .databrickscfg does not exist
1717
[DEFAULT]
1818

1919
[custom-test]
20-
host = [DATABRICKS_URL]
21-
auth_type = databricks-cli
20+
host = [DATABRICKS_URL]
21+
auth_type = databricks-cli
22+
workspace_id = [NUMID]

acceptance/cmd/auth/login/host-arg-overrides-profile/out.databrickscfg

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
[DEFAULT]
33

44
[override-test]
5-
host = [DATABRICKS_URL]
6-
auth_type = databricks-cli
5+
host = [DATABRICKS_URL]
6+
workspace_id = [NUMID]
7+
auth_type = databricks-cli

acceptance/cmd/auth/login/host-arg-overrides-profile/output.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ Profile override-test was successfully saved
1212
[DEFAULT]
1313

1414
[override-test]
15-
host = [DATABRICKS_URL]
16-
auth_type = databricks-cli
15+
host = [DATABRICKS_URL]
16+
workspace_id = [NUMID]
17+
auth_type = databricks-cli

acceptance/cmd/auth/login/host-from-profile/out.databrickscfg

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
[DEFAULT]
33

44
[existing-profile]
5-
host = [DATABRICKS_URL]
6-
auth_type = databricks-cli
5+
host = [DATABRICKS_URL]
6+
workspace_id = [NUMID]
7+
auth_type = databricks-cli

acceptance/cmd/auth/login/host-from-profile/output.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ Profile existing-profile was successfully saved
1212
[DEFAULT]
1313

1414
[existing-profile]
15-
host = [DATABRICKS_URL]
16-
auth_type = databricks-cli
15+
host = [DATABRICKS_URL]
16+
workspace_id = [NUMID]
17+
auth_type = databricks-cli

acceptance/cmd/auth/login/nominal/out.databrickscfg

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
[DEFAULT]
33

44
[test]
5-
host = [DATABRICKS_URL]
6-
auth_type = databricks-cli
5+
host = [DATABRICKS_URL]
6+
workspace_id = [NUMID]
7+
auth_type = databricks-cli
78

89
[__settings__]
910
default_profile = test

acceptance/cmd/auth/login/preserve-fields/output.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ cluster_id = existing-cluster-123
1818
warehouse_id = warehouse-456
1919
azure_environment = USGOVERNMENT
2020
custom_key = my-custom-value
21+
workspace_id = [NUMID]
2122
auth_type = databricks-cli

0 commit comments

Comments
 (0)