Skip to content

[Feature] Implement U2M Authentication in the Go SDK #1108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 50 commits into from
Mar 24, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
4a538e3
work
mgyucht Jan 3, 2025
70ef496
Merge branch 'main' into move-u2m-to-sdks
mgyucht Jan 3, 2025
3550329
fix tests
mgyucht Jan 3, 2025
05bd576
Merge branch 'main' into move-u2m-to-sdks
mgyucht Jan 3, 2025
0688c3b
fix test
mgyucht Jan 3, 2025
35b8365
respond to some changes
mgyucht Jan 6, 2025
47f2fb2
more work
mgyucht Jan 6, 2025
e883616
work
mgyucht Jan 6, 2025
181f88b
basic tests
mgyucht Jan 6, 2025
74592c3
some tests
mgyucht Jan 6, 2025
9b5913c
one more test case
mgyucht Jan 6, 2025
73c73af
more tests
mgyucht Jan 6, 2025
63bf521
comment
mgyucht Jan 6, 2025
2622989
mutex
mgyucht Jan 6, 2025
359a5d0
documentation
mgyucht Jan 6, 2025
44af89f
docs
mgyucht Jan 6, 2025
bd7303e
fix test names
mgyucht Jan 6, 2025
323fb61
work
mgyucht Jan 7, 2025
3905c5d
fix tests
mgyucht Jan 7, 2025
f53fb84
comments and test fixes
mgyucht Jan 7, 2025
ef8c3f3
fix
mgyucht Jan 7, 2025
0ff447d
fix
mgyucht Jan 7, 2025
6c2ed96
work
mgyucht Jan 7, 2025
e9f3732
better error message
mgyucht Jan 8, 2025
19a34ce
fix test
mgyucht Jan 8, 2025
c7b5155
fix
mgyucht Jan 8, 2025
37b44b1
tweak
mgyucht Jan 8, 2025
36ea3dc
work
mgyucht Jan 16, 2025
fc87393
fix test
mgyucht Jan 17, 2025
945151f
undo move
mgyucht Jan 17, 2025
356a7c9
remove extra context
mgyucht Jan 20, 2025
7441bd1
work
mgyucht Jan 28, 2025
3550bba
address comment
mgyucht Jan 30, 2025
fcb031f
fix
mgyucht Jan 30, 2025
83e4141
more tweaks
mgyucht Jan 30, 2025
51a5b08
work
mgyucht Jan 30, 2025
62acd68
simpler
mgyucht Jan 30, 2025
f506510
tweaks
mgyucht Jan 30, 2025
d70dac4
fix test
mgyucht Jan 30, 2025
373daf3
Merge branch 'main' into move-u2m-to-sdks
mgyucht Feb 14, 2025
2f0ebbb
work
mgyucht Feb 14, 2025
21df9ec
Merge branch 'main' into move-u2m-to-sdks
mgyucht Feb 17, 2025
6ff1f3d
fmt
mgyucht Feb 17, 2025
f08c2b9
work
mgyucht Feb 17, 2025
de60fcf
fix
mgyucht Feb 17, 2025
11380f0
remove
mgyucht Feb 17, 2025
44c2ca7
fix
mgyucht Feb 17, 2025
45b7e55
changelog
mgyucht Feb 18, 2025
51da35a
Merge branch 'main' into move-u2m-to-sdks
mgyucht Mar 21, 2025
e37d457
clean up
mgyucht Mar 21, 2025
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
123 changes: 0 additions & 123 deletions config/auth_databricks_cli.go

This file was deleted.

108 changes: 0 additions & 108 deletions config/auth_databricks_cli_test.go

This file was deleted.

2 changes: 1 addition & 1 deletion config/auth_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var authProviders = []CredentialsStrategy{
PatCredentials{},
BasicCredentials{},
M2mCredentials{},
DatabricksCliCredentials{},
U2MCredentials{},
MetadataServiceCredentials{},

// Attempt to configure auth from most specific to most generic (the Azure CLI).
Expand Down
33 changes: 1 addition & 32 deletions config/auth_m2m.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,15 @@ package config

import (
"context"
"errors"
"fmt"

"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"

"github.com/databricks/databricks-sdk-go/credentials"
"github.com/databricks/databricks-sdk-go/httpclient"
"github.com/databricks/databricks-sdk-go/logger"
)

var errOAuthNotSupported = errors.New("databricks OAuth is not supported for this host")

type M2mCredentials struct {
}

Expand All @@ -26,7 +22,7 @@ func (c M2mCredentials) Configure(ctx context.Context, cfg *Config) (credentials
if cfg.ClientID == "" || cfg.ClientSecret == "" {
return nil, nil
}
endpoints, err := oidcEndpoints(ctx, cfg)
endpoints, err := cfg.refreshClient.GetOidcEndpoints(ctx, cfg.Host, cfg.AccountID)
if err != nil {
return nil, fmt.Errorf("oidc: %w", err)
}
Expand All @@ -41,30 +37,3 @@ func (c M2mCredentials) Configure(ctx context.Context, cfg *Config) (credentials
visitor := refreshableVisitor(ts)
return credentials.NewOAuthCredentialsProvider(visitor, ts.Token), nil
}

func oidcEndpoints(ctx context.Context, cfg *Config) (*oauthAuthorizationServer, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

prefix := cfg.Host
if cfg.IsAccountClient() && cfg.AccountID != "" {
// TODO: technically, we could use the same config profile for both workspace
// and account, but we have to add logic for determining accounts host from
// workspace host.
prefix := fmt.Sprintf("%s/oidc/accounts/%s", cfg.Host, cfg.AccountID)
return &oauthAuthorizationServer{
AuthorizationEndpoint: fmt.Sprintf("%s/v1/authorize", prefix),
TokenEndpoint: fmt.Sprintf("%s/v1/token", prefix),
}, nil
}
oidc := fmt.Sprintf("%s/oidc/.well-known/oauth-authorization-server", prefix)
var oauthEndpoints oauthAuthorizationServer
err := cfg.refreshClient.Do(ctx, "GET", oidc,
httpclient.WithResponseUnmarshal(&oauthEndpoints))
if err != nil {
return nil, errOAuthNotSupported
}
return &oauthEndpoints, nil
}

type oauthAuthorizationServer struct {
AuthorizationEndpoint string `json:"authorization_endpoint"` // ../v1/authorize
TokenEndpoint string `json:"token_endpoint"` // ../v1/token
}
5 changes: 3 additions & 2 deletions config/auth_m2m_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"net/url"
"testing"

"github.com/databricks/databricks-sdk-go/httpclient"
"github.com/databricks/databricks-sdk-go/httpclient/fixtures"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
Expand All @@ -16,7 +17,7 @@ func TestM2mHappyFlow(t *testing.T) {
ClientSecret: "c",
HTTPTransport: fixtures.MappingTransport{
"GET /oidc/.well-known/oauth-authorization-server": {
Response: oauthAuthorizationServer{
Response: httpclient.OAuthAuthorizationServer{
AuthorizationEndpoint: "https://localhost:1234/dummy/auth",
TokenEndpoint: "https://localhost:1234/dummy/token",
},
Expand Down Expand Up @@ -80,5 +81,5 @@ func TestM2mNotSupported(t *testing.T) {
},
},
})
require.ErrorIs(t, err, errOAuthNotSupported)
require.ErrorIs(t, err, httpclient.ErrOAuthNotSupported)
}
59 changes: 59 additions & 0 deletions config/auth_u2m.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package config

import (
"context"
"fmt"
"net/http"

"github.com/databricks/databricks-sdk-go/credentials"
"github.com/databricks/databricks-sdk-go/credentials/oauth"
"github.com/databricks/databricks-sdk-go/logger"
)

type U2MCredentials struct {
Auth *oauth.PersistentAuth
}

// Name implements CredentialsStrategy.
func (u U2MCredentials) Name() string {
return "oauth-u2m"
}

// Configure implements CredentialsStrategy.
func (u U2MCredentials) Configure(ctx context.Context, cfg *Config) (credentials.CredentialsProvider, error) {
a := u.Auth
if a == nil {
var err error
a, err = oauth.NewPersistentAuth(ctx)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we update u.auth?

if err != nil {
logger.Debugf(ctx, "failed to create persistent auth: %v, continuing", err)
return nil, nil
}
}
f := func(r *http.Request) error {
arg := oauth.BasicOAuthArgument{
Host: cfg.Host,
AccountID: cfg.AccountID,
}
token, err := a.Load(r.Context(), arg)
if err != nil {
return fmt.Errorf("oidc: %w", err)
}
r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.AccessToken))
return nil
}

r, err := http.NewRequestWithContext(ctx, http.MethodGet, "", nil)
if err != nil {
return nil, fmt.Errorf("http request: %w", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

[nit] Move this on top of the f declaration so that f is closer from the place it is called. There's also no need for f if this fails.

// Try to load the credential from the token cache. If absent, fall back
// to the next credentials strategy.
if err := f(r); err != nil {
return nil, nil
}

return credentials.NewCredentialsProvider(f), nil
}

var _ CredentialsStrategy = U2MCredentials{}
10 changes: 10 additions & 0 deletions credentials/cache/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package cache

import (
"golang.org/x/oauth2"
)

type TokenCache interface {
Store(key string, t *oauth2.Token) error
Lookup(key string) (*oauth2.Token, error)
}
Loading
Loading