Skip to content

Add discovery login via login.databricks.com#4702

Open
simonfaltum wants to merge 16 commits intomainfrom
simonfaltum/login-discovery
Open

Add discovery login via login.databricks.com#4702
simonfaltum wants to merge 16 commits intomainfrom
simonfaltum/login-discovery

Conversation

@simonfaltum
Copy link
Member

Why

When users run databricks auth login without specifying a host, the CLI had no fallback. Users had to know their workspace URL upfront. This adds a discovery flow via login.databricks.com where users authenticate in the browser, select a workspace, and the CLI automatically discovers the workspace URL.

Changes

Before: databricks auth login without --host prompted for a host URL or failed.
Now: databricks auth login without --host opens login.databricks.com in the browser. After the user authenticates and selects a workspace, the CLI saves the profile with the discovered host, account ID, and workspace ID.

Implementation details:

  • shouldUseDiscovery() detects when no host is available from flags, args, or existing profile
  • discoveryLogin() orchestrates the browser-based flow using the SDK's discovery OAuth support
  • IntrospectToken() (in libs/auth) calls the workspace introspection endpoint to extract account/workspace IDs (best-effort, non-fatal on failure)
  • splitScopes() deduplicates scope parsing across login paths
  • Error messages include actionable tips (e.g., "you can specify a workspace directly with: databricks auth login --host ")
  • Discovery dependencies (PersistentAuth, OAuthArgument, IntrospectToken) are injectable via package-level vars for testability

Test plan

  • Unit tests for shouldUseDiscovery (table-driven, 6 cases)
  • Unit tests for splitScopes (empty, single, whitespace, empty entries)
  • Unit tests for IntrospectToken (success, zero workspace ID, HTTP errors, malformed JSON, auth header, endpoint path)
  • Integration test for discoveryLogin with introspection failure (verifies profile is still saved)
  • Integration test for discoveryLogin with introspection success (verifies metadata extraction)
  • go test ./cmd/auth/... ./libs/auth/... passes

simonfaltum and others added 3 commits March 11, 2026 09:43
Add IntrospectToken() which calls /api/2.0/tokens/introspect to extract
account_id and workspace_id from workspace tokens. This will be used by
the discovery login flow to enrich profiles with metadata.

Note: go.mod contains a temporary replace directive pointing to the local
SDK worktree with discovery changes. This will be reverted before the PR.
Extract shouldUseDiscovery() routing function and test it directly with
table-driven tests instead of executing the full cobra command, which
hangs waiting for an OAuth browser callback that never arrives in tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…essages

Extract splitScopes helper to deduplicate scope parsing. Save scopes to
profile in discoveryLogin (was previously dropped). Add actionable error
messages with --host tip for setup failures and config file path for save
failures. Make discoveryLogin testable by introducing package-level vars
for PersistentAuth, OAuthArgument, and IntrospectToken. Fix typo in
introspect test data. Use http.MethodGet constant and drain response body
on error for TCP connection reuse.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@eng-dev-ecosystem-bot
Copy link
Collaborator

eng-dev-ecosystem-bot commented Mar 11, 2026

Commit: 5a21b82

Run: 23060092217

Env 🔄​flaky 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
💚​ aws linux 8 7 268 787 6:37
💚​ aws windows 8 7 270 785 4:56
💚​ aws-ucws linux 8 7 365 702 8:04
🔄​ aws-ucws windows 2 7 7 366 700 6:37
💚​ azure linux 2 9 271 785 5:31
💚​ azure windows 2 9 273 783 4:55
🔄​ azure-ucws linux 2 1 9 369 698 7:56
💚​ azure-ucws windows 2 9 372 696 5:49
💚​ gcp linux 2 9 267 788 5:30
💚​ gcp windows 2 9 269 786 5:30
16 interesting tests: 7 SKIP, 6 RECOVERED, 3 flaky
Test Name aws linux aws windows aws-ucws linux aws-ucws windows azure linux azure windows azure-ucws linux azure-ucws windows gcp linux gcp windows
🔄​ TestAccept 💚​R 💚​R 💚​R 🔄​f 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/resources/permissions 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions 💚​R 💚​R 💚​R 💚​R 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=direct 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=terraform 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions 💚​R 💚​R 💚​R 💚​R 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=direct 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=terraform 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/resources/postgres_branches/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/recreate 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/update_protected 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/without_branch_id 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_endpoints/recreate 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/synced_database_tables/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🔄​ TestAccept/ssh/connect-serverless-gpu 🙈​s 🙈​s ✅​p 🔄​f 🙈​s 🙈​s 🔄​f ✅​p 🙈​s 🙈​s
🔄​ TestAccept/ssh/connection 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 🔄​f 💚​R 💚​R 💚​R
Top 21 slowest tests (at least 2 minutes):
duration env testname
4:17 aws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:03 aws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:40 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:38 azure-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:17 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:15 aws-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:15 azure windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:12 aws-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:05 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:04 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:46 aws-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:45 aws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:40 aws-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:18 azure linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:13 azure-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:13 aws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:07 azure-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:07 azure windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:06 azure linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:05 azure-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:02 aws-ucws linux TestAccept/ssh/connect-serverless-gpu

Group same-type function parameters and use strconv.FormatInt instead of
fmt.Sprintf for integer conversion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract repeated discovery fallback tip to discoveryFallbackTip const
- Reuse splitScopes in token.go instead of inline split+trim (also fixes
  missing empty-entry filtering)
- Rename TestDiscoveryLogin_IntrospectionSuccessExtractsMetadata to
  TestIntrospectToken_SuccessExtractsMetadata (it tests IntrospectToken
  directly, not discoveryLogin)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The SDKs are not yet ready to handle account_id on workspace profiles
as part of the cache key. Saving it now would break existing auth flows.
Keeping the introspection call so workspace_id is still extracted.

Co-authored-by: Isaac
…mismatch

Reject --configure-cluster and --configure-serverless when discovery login
is used (no host available), since these flags require a known workspace.

Warn when the introspected account_id differs from the existing profile's
account_id to surface potential misconfigurations early.

Also use the discoveryFallbackTip constant instead of hardcoded strings.

Co-authored-by: Isaac
Test that a warning is logged when the introspected account_id differs
from the existing profile's account_id, and that no warning is emitted
when they match.

Co-authored-by: Isaac
- Remove TestIntrospectToken_ServerError (same code path as _HTTPError)
- Merge TestIntrospectToken_VerifyAuthHeader and _VerifyEndpoint into one
- Remove TestIntrospectToken_SuccessExtractsMetadata from login_test.go
  (duplicate of introspect_test.go:TestIntrospectToken_Success)

Co-authored-by: Isaac
Follow the codebase convention of using the context-aware env package
for environment variable access.

Co-authored-by: Isaac
The existing non-discovery login path also used os.Getenv for
DATABRICKS_CONFIG_FILE. Replaced with the context-aware env package
to follow codebase conventions and enable test overrides.

Co-authored-by: Isaac
Copy link
Contributor

@shreyas-goenka shreyas-goenka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This review was posted by Claude (AI assistant). Shreyas will do a separate, more thorough review pass.

Priority: MEDIUM — Well-structured with a few concerns

MEDIUM: http.DefaultClient in IntrospectToken bypasses SDK HTTP configuration

libs/auth/introspect.go uses http.DefaultClient, bypassing proxy, custom TLS, retry, and timeout settings configured in the SDK. In enterprise environments, this call will fail. Since introspection is best-effort, this is acceptable for v1 but should be tracked for improvement.

LOW: Missing discoveredHost validation

After arg.GetDiscoveredHost(), there's no check that the returned host is non-empty. This could save an empty host string to the profile.

LOW: Missing IsUnifiedHost in discovery profile save

The discovery path doesn't set Experimental_IsUnifiedHost nor clear experimental_is_unified_host, unlike the non-discovery path.

DESIGN: Verify introspection endpoint supports GET

The code uses http.MethodGet for /api/2.0/tokens/introspect. If this follows RFC 7662 (which expects POST), the call will silently fail with 405.

What looks good

  • Clean discoveryPersistentAuth narrow interface
  • shouldUseDiscovery is pure and testable (6 table-driven test cases)
  • splitScopes extraction reduces duplication
  • Best-effort introspection doesn't block login on failure
  • env.Get(ctx, ...) replaces os.Getenv correctly

After GetDiscoveredHost(), check that the returned host is not empty
before saving it to the profile. An empty host would produce a broken
profile entry. Return a clear error with the fallback tip so the user
knows they can retry with --host.
…ithout --host in discovery login

These flags are meaningless in discovery mode (login.databricks.com) and
silently ignored before this change. For example, 'databricks auth login
--account-id <id>' would go through workspace discovery but getProfileName()
would generate an ACCOUNT-<id> profile name for a workspace token.

Now the CLI returns a clear error telling the user to provide --host when
any of these flags are explicitly set.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants