Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions cmd/auth/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,27 @@ func loadToken(ctx context.Context, args loadTokenArgs) (*oauth2.Token, error) {
return nil, err
}
args.profileName = selected
existingProfile, err = loadProfileByName(ctx, selected, args.profiler)
if err != nil {
return nil, err
}
} else if len(matchingProfiles) == 1 {
args.profileName = matchingProfiles[0].Name
existingProfile = &matchingProfiles[0]
}
}

// Check if the resolved profile uses M2M authentication (client credentials).
// The auth token command only supports U2M OAuth tokens.
if existingProfile != nil && existingProfile.HasClientCredentials {
return nil, fmt.Errorf(
"profile %q uses M2M authentication (client_id/client_secret). "+
"`databricks auth token` only supports U2M (user-to-machine) authentication tokens. "+
"To authenticate as a service principal, use the Databricks SDK directly",
args.profileName,
)
}

args.authArguments.Profile = args.profileName

ctx, cancel := context.WithTimeout(ctx, args.tokenTimeout)
Expand Down
46 changes: 46 additions & 0 deletions cmd/auth/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ func TestToken_loadToken(t *testing.T) {
Name: "legacy-ws",
Host: "https://legacy-ws.cloud.databricks.com",
},
{
Name: "m2m-profile",
Host: "https://m2m.cloud.databricks.com",
HasClientCredentials: true,
},
},
}
tokenCache := &inMemoryTokenCache{
Expand Down Expand Up @@ -526,6 +531,47 @@ func TestToken_loadToken(t *testing.T) {
},
wantErr: "no profiles configured. Run 'databricks auth login' to create a profile",
},
{
name: "M2M profile returns clear error",
args: loadTokenArgs{
authArguments: &auth.AuthArguments{},
profileName: "m2m-profile",
args: []string{},
tokenTimeout: 1 * time.Hour,
profiler: profiler,
},
wantErr: `profile "m2m-profile" uses M2M authentication (client_id/client_secret). ` +
"`databricks auth token` only supports U2M (user-to-machine) authentication tokens. " +
"To authenticate as a service principal, use the Databricks SDK directly",
},
{
name: "M2M profile detected via positional arg",
args: loadTokenArgs{
authArguments: &auth.AuthArguments{},
profileName: "",
args: []string{"m2m-profile"},
tokenTimeout: 1 * time.Hour,
profiler: profiler,
},
wantErr: `profile "m2m-profile" uses M2M authentication (client_id/client_secret). ` +
"`databricks auth token` only supports U2M (user-to-machine) authentication tokens. " +
"To authenticate as a service principal, use the Databricks SDK directly",
},
{
name: "M2M profile detected via host resolution",
args: loadTokenArgs{
authArguments: &auth.AuthArguments{
Host: "https://m2m.cloud.databricks.com",
},
profileName: "",
args: []string{},
tokenTimeout: 1 * time.Hour,
profiler: profiler,
},
wantErr: `profile "m2m-profile" uses M2M authentication (client_id/client_secret). ` +
"`databricks auth token` only supports U2M (user-to-machine) authentication tokens. " +
"To authenticate as a service principal, use the Databricks SDK directly",
},
{
name: "no args, DATABRICKS_HOST env resolves",
setupCtx: func(ctx context.Context) context.Context {
Expand Down
15 changes: 8 additions & 7 deletions libs/databrickscfg/profile/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,14 @@ func (f FileProfilerImpl) LoadProfiles(ctx context.Context, fn ProfileMatchFunct
continue
}
profile := Profile{
Name: v.Name(),
Host: host,
AccountID: all["account_id"],
WorkspaceID: all["workspace_id"],
IsUnifiedHost: all["experimental_is_unified_host"] == "true",
ClusterID: all["cluster_id"],
ServerlessComputeID: all["serverless_compute_id"],
Name: v.Name(),
Host: host,
AccountID: all["account_id"],
WorkspaceID: all["workspace_id"],
IsUnifiedHost: all["experimental_is_unified_host"] == "true",
ClusterID: all["cluster_id"],
ServerlessComputeID: all["serverless_compute_id"],
HasClientCredentials: all["client_id"] != "" && all["client_secret"] != "",
}
if fn(profile) {
profiles = append(profiles, profile)
Expand Down
15 changes: 8 additions & 7 deletions libs/databrickscfg/profile/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import (
// It should only be used for prompting and filtering.
// Use its name to construct a config.Config.
type Profile struct {
Name string
Host string
AccountID string
WorkspaceID string
IsUnifiedHost bool
ClusterID string
ServerlessComputeID string
Name string
Host string
AccountID string
WorkspaceID string
IsUnifiedHost bool
ClusterID string
ServerlessComputeID string
HasClientCredentials bool
}

func (p Profile) Cloud() string {
Expand Down