Skip to content

Commit

Permalink
Address comment
Browse files Browse the repository at this point in the history
  • Loading branch information
shinfan committed Jun 17, 2021
1 parent 96893f9 commit 520006d
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 41 deletions.
53 changes: 25 additions & 28 deletions internal/creds.go
Expand Up @@ -65,55 +65,52 @@ const (

// credentialsFromJSON returns a google.Credentials based on the input.
//
// - A standard OAuth 2.0 flow will be executed if at least one of the
// following conditions is met:
// (1) the scope is non-empty and the scope for self-signed JWT flow is
// disabled.
// (2) Service Account Impersontation
// - A self-signed JWT flow will be executed if the following conditions are
// met:
// (1) Either the scope for self-signed JWT flow is enabled or audiences are
// explicitly provided by users.
// (2) No service account impersontation
//
// - Otherwise, executes a self-signed JWT flow (google.aip.dev/auth/4111)
// - Otherwise, executes standard OAuth 2.0 flow
// More details: google.aip.dev/auth/4111
func credentialsFromJSON(ctx context.Context, data []byte, ds *DialSettings) (*google.Credentials, error) {
// By default, a standard OAuth 2.0 token source is created
cred, err := google.CredentialsFromJSON(ctx, data, ds.GetScopes()...)
if err != nil {
return nil, err
}
if isOAuthFlow(data, ds) {
// Standard OAuth 2.0 Flow
return cred, nil
}

isJWTFlow, err := isSelfSignedJWTFlow(cred.JSON)
// Override the token source to use self-signed JWT if conditions are met
isJWTFlow, err := isSelfSignedJWTFlow(cred.JSON, ds)
if err != nil {
return nil, err
}

if isJWTFlow {
ts, err := selfSignedJWTTokenSource(data, ds.GetAudience(), ds.GetScopes())
if err != nil {
return nil, err
}
cred.TokenSource = ts
}
return cred, err
}

func isOAuthFlow(data []byte, ds *DialSettings) bool {
// Standard OAuth 2.0 Flow
return len(data) == 0 ||
(len(ds.GetScopes()) > 0 && !ds.EnableScopeForJWT) ||
ds.ImpersonationConfig != nil
return cred, err
}

func isSelfSignedJWTFlow(data []byte) (bool, error) {
// Check if JSON is a service account and if so create a self-signed JWT.
var f struct {
Type string `json:"type"`
// The rest JSON fields are omitted because they are not used.
}
if err := json.Unmarshal(data, &f); err != nil {
return false, err
func isSelfSignedJWTFlow(data []byte, ds *DialSettings) (bool, error) {
if (ds.UseJwtWithScope || len(ds.Audiences) > 0) &&
ds.ImpersonationConfig == nil {
// Check if JSON is a service account and if so create a self-signed JWT.
var f struct {
Type string `json:"type"`
// The rest JSON fields are omitted because they are not used.
}
if err := json.Unmarshal(data, &f); err != nil {
return false, err
}
return f.Type == serviceAccountKey, nil
} else {
return false, nil
}
return f.Type == serviceAccountKey, nil
}

func selfSignedJWTTokenSource(data []byte, audience string, scopes []string) (oauth2.TokenSource, error) {
Expand Down
12 changes: 6 additions & 6 deletions internal/creds_test.go
Expand Up @@ -98,9 +98,9 @@ func TestJWTWithScope(t *testing.T) {
// Load a valid JSON file. No way to really test the contents; we just
// verify that there is no error.
ds := &DialSettings{
CredentialsFile: "testdata/service-account.json",
Scopes: []string{"foo"},
EnableScopeForJWT: true,
CredentialsFile: "testdata/service-account.json",
Scopes: []string{"foo"},
UseJwtWithScope: true,
}
if _, err := Creds(ctx, ds); err != nil {
t.Errorf("got %v, wanted no error", err)
Expand All @@ -109,9 +109,9 @@ func TestJWTWithScope(t *testing.T) {
// Load valid JSON. No way to really test the contents; we just
// verify that there is no error.
ds = &DialSettings{
CredentialsJSON: []byte(validServiceAccountJSON),
Scopes: []string{"foo"},
EnableScopeForJWT: true,
CredentialsJSON: []byte(validServiceAccountJSON),
Scopes: []string{"foo"},
UseJwtWithScope: true,
}
if _, err := Creds(ctx, ds); err != nil {
t.Errorf("got %v, wanted no error", err)
Expand Down
2 changes: 1 addition & 1 deletion internal/settings.go
Expand Up @@ -24,7 +24,7 @@ type DialSettings struct {
DefaultMTLSEndpoint string
Scopes []string
DefaultScopes []string
EnableScopeForJWT bool
UseJwtWithScope bool
TokenSource oauth2.TokenSource
Credentials *google.Credentials
CredentialsFile string // if set, Token Source is ignored.
Expand Down
12 changes: 6 additions & 6 deletions option/internaloption/internaloption.go
Expand Up @@ -95,14 +95,14 @@ func (w withDefaultScopes) Apply(o *internal.DialSettings) {
copy(o.DefaultScopes, w)
}

// EnableScopeForJWT returns a ClientOption that specifies if scope can be used
// UseJwtWithScope returns a ClientOption that specifies if scope can be used
// with self-signed JWT.
func EnableScopeForJWT(scopeForJWT bool) ClientOption {
return enableScopeForJWT(audience)
func UseJwtWithScope(useScope bool) option.ClientOption {
return useJwtWithScope(useScope)
}

type enableScopeForJWT bool
type useJwtWithScope bool

func (w enableScopeForJWT) Apply(o *internal.DialSettings) {
o.EnableScopeForJWT = w
func (w useJwtWithScope) Apply(o *internal.DialSettings) {
o.UseJwtWithScope = bool(w)
}

0 comments on commit 520006d

Please sign in to comment.