Skip to content

Commit

Permalink
fix(auth): support gRPC API keys (#10460)
Browse files Browse the repository at this point in the history
Porting googleapis/google-api-go-client#2326
which happened after we had made parity change to new auth lib.
  • Loading branch information
codyoss authored Jul 1, 2024
1 parent 66b8efe commit daa6646
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 3 deletions.
37 changes: 34 additions & 3 deletions auth/grpctransport/grpctransport.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ type Options struct {
// configured for the client, which will be compared to the universe domain
// that is separately configured for the credentials.
UniverseDomain string
// APIKey specifies an API key to be used as the basis for authentication.
// If set DetectOpts are ignored.
APIKey string

// InternalOptions are NOT meant to be set directly by consumers of this
// package, they should only be set by generated client code.
Expand All @@ -109,7 +112,8 @@ func (o *Options) validate() error {
if o.InternalOptions != nil && o.InternalOptions.SkipValidation {
return nil
}
hasCreds := o.Credentials != nil ||
hasCreds := o.APIKey != "" ||
o.Credentials != nil ||
(o.DetectOpts != nil && len(o.DetectOpts.CredentialsJSON) > 0) ||
(o.DetectOpts != nil && o.DetectOpts.CredentialsFile != "")
if o.DisableAuthentication && hasCreds {
Expand Down Expand Up @@ -237,8 +241,15 @@ func dial(ctx context.Context, secure bool, opts *Options) (*grpc.ClientConn, er
return nil, err
}

// Authentication can only be sent when communicating over a secure connection.
if !opts.DisableAuthentication {
if opts.APIKey != "" {
grpcOpts = append(grpcOpts,
grpc.WithPerRPCCredentials(&grpcKeyProvider{
apiKey: opts.APIKey,
metadata: opts.Metadata,
secure: secure,
}),
)
} else if !opts.DisableAuthentication {
metadata := opts.Metadata

var creds *auth.Credentials
Expand Down Expand Up @@ -283,6 +294,26 @@ func dial(ctx context.Context, secure bool, opts *Options) (*grpc.ClientConn, er
return grpc.DialContext(ctx, endpoint, grpcOpts...)
}

// grpcKeyProvider satisfies https://pkg.go.dev/google.golang.org/grpc/credentials#PerRPCCredentials.
type grpcKeyProvider struct {
apiKey string
metadata map[string]string
secure bool
}

func (g *grpcKeyProvider) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
metadata := make(map[string]string, len(g.metadata)+1)
metadata["X-goog-api-key"] = g.apiKey
for k, v := range g.metadata {
metadata[k] = v
}
return metadata, nil
}

func (g *grpcKeyProvider) RequireTransportSecurity() bool {
return g.secure
}

// grpcCredentialsProvider satisfies https://pkg.go.dev/google.golang.org/grpc/credentials#PerRPCCredentials.
type grpcCredentialsProvider struct {
creds *auth.Credentials
Expand Down
22 changes: 22 additions & 0 deletions auth/grpctransport/grpctransport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,28 @@ func TestNewClient_DetectedServiceAccount(t *testing.T) {
}
}

func TestGRPCKeyProvider_GetRequestMetadata(t *testing.T) {
apiKey := "MY_API_KEY"
reason := "MY_REQUEST_REASON"
ts := grpcKeyProvider{
apiKey: apiKey,
metadata: map[string]string{
"X-goog-request-reason": reason,
},
}
got, err := ts.GetRequestMetadata(context.Background())
if err != nil {
t.Fatal(err)
}
want := map[string]string{
"X-goog-api-key": ts.apiKey,
"X-goog-request-reason": reason,
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
}
}

type staticTP struct {
tok *auth.Token
}
Expand Down

0 comments on commit daa6646

Please sign in to comment.