Skip to content

Commit

Permalink
fix(auth/oauth2adapt): convert token metadata where possible (#11062)
Browse files Browse the repository at this point in the history
This was missed in the orginal implmentation. The code is still not
prefect as converting from an oauth2.Token to auth.Token is lossy
because there is no way to extract out the whole metadata context.
Converting from auth.Token -> oauth2.Token is not lossy though.

This bug was discovered because some direct path checks look for
theses metadata values. Just to be safe we will preserve both forms
of these values in the final token metadata, if present.

Internal Bug: 376281562
  • Loading branch information
codyoss authored Oct 30, 2024
1 parent 9979e72 commit 34bf1c1
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 13 deletions.
41 changes: 36 additions & 5 deletions auth/oauth2adapt/oauth2adapt.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ import (
"golang.org/x/oauth2/google"
)

const (
oauth2TokenSourceKey = "oauth2.google.tokenSource"
oauth2ServiceAccountKey = "oauth2.google.serviceAccount"
authTokenSourceKey = "auth.google.tokenSource"
authServiceAccountKey = "auth.google.serviceAccount"
)

// TokenProviderFromTokenSource converts any [golang.org/x/oauth2.TokenSource]
// into a [cloud.google.com/go/auth.TokenProvider].
func TokenProviderFromTokenSource(ts oauth2.TokenSource) auth.TokenProvider {
Expand All @@ -47,10 +54,21 @@ func (tp *tokenProviderAdapter) Token(context.Context) (*auth.Token, error) {
}
return nil, err
}
// Preserve compute token metadata, for both types of tokens.
metadata := map[string]interface{}{}
if val, ok := tok.Extra(oauth2TokenSourceKey).(string); ok {
metadata[authTokenSourceKey] = val
metadata[oauth2TokenSourceKey] = val
}
if val, ok := tok.Extra(oauth2ServiceAccountKey).(string); ok {
metadata[authServiceAccountKey] = val
metadata[oauth2ServiceAccountKey] = val
}
return &auth.Token{
Value: tok.AccessToken,
Type: tok.Type(),
Expiry: tok.Expiry,
Value: tok.AccessToken,
Type: tok.Type(),
Expiry: tok.Expiry,
Metadata: metadata,
}, nil
}

Expand All @@ -76,11 +94,24 @@ func (ts *tokenSourceAdapter) Token() (*oauth2.Token, error) {
}
return nil, err
}
return &oauth2.Token{
tok2 := &oauth2.Token{
AccessToken: tok.Value,
TokenType: tok.Type,
Expiry: tok.Expiry,
}, nil
}
// Preserve token metadata.
metadata := tok.Metadata
if metadata != nil {
// Append compute token metadata in converted form.
if val, ok := metadata[authTokenSourceKey].(string); ok && val != "" {
metadata[oauth2TokenSourceKey] = val
}
if val, ok := metadata[authServiceAccountKey].(string); ok && val != "" {
metadata[oauth2ServiceAccountKey] = val
}
tok2 = tok2.WithExtra(metadata)
}
return tok2, nil
}

// AuthCredentialsFromOauth2Credentials converts a [golang.org/x/oauth2/google.Credentials]
Expand Down
71 changes: 63 additions & 8 deletions auth/oauth2adapt/oauth2adapt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,67 @@ func TestOauth2CredentialsFromAuthCredentials(t *testing.T) {
}
}

func TestMetadataConversions_ToTokenProvider(t *testing.T) {
ctx := context.Background()
tok := &oauth2.Token{AccessToken: "token"}
tok = tok.WithExtra(map[string]interface{}{
oauth2TokenSourceKey: "compute-metadata",
oauth2ServiceAccountKey: "default",
})
inputCreds := &google.Credentials{
ProjectID: "test_project",
TokenSource: tokenSource{token: tok},
JSON: []byte("json"),
UniverseDomainProvider: func() (string, error) {
return "domain", nil
},
}
outCreds := AuthCredentialsFromOauth2Credentials(inputCreds)
tok2, err := outCreds.Token(ctx)
if err != nil {
t.Fatal(err)
}
if tok2.MetadataString(authTokenSourceKey) != "compute-metadata" {
t.Errorf("got %q, want %q", tok2.MetadataString(authTokenSourceKey), "compute-metadata")
}
if tok2.MetadataString(oauth2TokenSourceKey) != "compute-metadata" {
t.Errorf("got %q, want %q", tok2.MetadataString(oauth2TokenSourceKey), "compute-metadata")
}
if tok2.MetadataString(authServiceAccountKey) != "default" {
t.Errorf("got %q, want %q", tok2.MetadataString(authTokenSourceKey), "default")
}
if tok2.MetadataString(oauth2ServiceAccountKey) != "default" {
t.Errorf("got %q, want %q", tok2.MetadataString(oauth2ServiceAccountKey), "default")
}
}

func TestMetadataConversions_ToTokenSource(t *testing.T) {
tok := &auth.Token{Value: "token", Metadata: map[string]interface{}{
authTokenSourceKey: "compute-metadata",
authServiceAccountKey: "default",
}}
inputCreds := auth.NewCredentials(&auth.CredentialsOptions{
TokenProvider: tokenProvider{token: tok},
})
outCreds := Oauth2CredentialsFromAuthCredentials(inputCreds)
tok2, err := outCreds.TokenSource.Token()
if err != nil {
t.Fatal(err)
}
if val, ok := tok2.Extra(oauth2TokenSourceKey).(string); !ok && val != "compute-metadata" {
t.Errorf("got %q, want %q", tok2.Extra(oauth2TokenSourceKey), "compute-metadata")
}
if val, ok := tok2.Extra(authTokenSourceKey).(string); !ok && val != "compute-metadata" {
t.Errorf("got %q, want %q", tok2.Extra(authTokenSourceKey), "compute-metadata")
}
if val, ok := tok2.Extra(oauth2ServiceAccountKey).(string); !ok && val != "default" {
t.Errorf("got %q, want %q", tok2.Extra(oauth2ServiceAccountKey), "default")
}
if val, ok := tok2.Extra(authServiceAccountKey).(string); !ok && val != "default" {
t.Errorf("got %q, want %q", tok2.Extra(authServiceAccountKey), "default")
}
}

type tokenSource struct {
token *oauth2.Token
err error
Expand All @@ -261,10 +322,7 @@ func (ts tokenSource) Token() (*oauth2.Token, error) {
if ts.err != nil {
return nil, ts.err
}
return &oauth2.Token{
AccessToken: ts.token.AccessToken,
TokenType: ts.token.TokenType,
}, nil
return ts.token, nil
}

type tokenProvider struct {
Expand All @@ -276,8 +334,5 @@ func (tp tokenProvider) Token(context.Context) (*auth.Token, error) {
if tp.err != nil {
return nil, tp.err
}
return &auth.Token{
Value: tp.token.Value,
Type: tp.token.Type,
}, nil
return tp.token, nil
}

0 comments on commit 34bf1c1

Please sign in to comment.