Skip to content

Commit

Permalink
feat(extension/googleclientauth): support id-token (#851)
Browse files Browse the repository at this point in the history
* feat(extension/googleclientauth): support id-token

Signed-off-by: Rintaro Okamura <rintaro.okamura@gmail.com>

* fix(extension/googleclientauth): add validation for token_format

Signed-off-by: Rintaro Okamura <rintaro.okamura@gmail.com>

* refactor(extensions/googleclientauth): use iota for token_format values

Signed-off-by: Rintaro Okamura <rintaro.okamura@gmail.com>

* fix(extensions/googleclientauth): use same credential

Signed-off-by: Rintaro Okamura <rintaro.okamura@gmail.com>

* fixup! refactor(extensions/googleclientauth): use iota for token_format values

Signed-off-by: Rintaro Okamura <rintaro.okamura@gmail.com>

* refactor(extension/googleclientauth): validate audience in validate
function

Signed-off-by: Rintaro Okamura <rintaro.okamura@gmail.com>

* refactor(extension/googleclientauth): remove redundant case

Signed-off-by: Rintaro Okamura <rintaro.okamura@gmail.com>

* refactor(extension/googleclientauth): remove enum

- and simplify token_type related codes

Signed-off-by: Rintaro Okamura <rintaro.okamura@gmail.com>

* test(extension/googleclientauth): add cases for id_token

Signed-off-by: Rintaro Okamura <rintaro.okamura@gmail.com>

* Update extension/googleclientauthextension/config.go

Co-authored-by: David Ashpole <dashpole@google.com>
Signed-off-by: Rintaro Okamura <rintaro.okamura@gmail.com>

---------

Signed-off-by: Rintaro Okamura <rintaro.okamura@gmail.com>
Co-authored-by: David Ashpole <dashpole@google.com>
  • Loading branch information
rinx and dashpole committed May 28, 2024
1 parent 4c2cadc commit 51ae889
Show file tree
Hide file tree
Showing 9 changed files with 296 additions and 6 deletions.
4 changes: 4 additions & 0 deletions extension/googleclientauthextension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ Following are the configuration fields:
- **project** - The Google Cloud Project telemetry is sent to if the gcp.project.id resource attribute is not set. If unspecified, this is determined using application default credentials.
- [**scopes**](https://datatracker.ietf.org/doc/html/rfc6749#section-3.3) - The oauth 2.0 scopes requested by the extension.
- [**quota_project**](https://cloud.google.com/apis/docs/system-parameters) - The project for quota and billing purposes. The caller must have serviceusage.services.use permission on the project.
- **token_type** - The type of generated token. Default: `access_token`
- `access_token`: [OAuth 2.0 access token](https://cloud.google.com/docs/authentication/token-types#access) will be generated.
- `id_token`: Google-signed [ID token](https://cloud.google.com/docs/authentication/token-types#id) will be generated.
- **audience** - The audience claim used for generating ID token
33 changes: 32 additions & 1 deletion extension/googleclientauthextension/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,24 @@
package googleclientauthextension // import "github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension"

import (
"errors"

"go.opentelemetry.io/collector/component"
)

const (
// accessToken indicates OAuth 2.0 access token (https://cloud.google.com/docs/authentication/token-types#access)
accessToken = "access_token"

// idToken indicates Google-signed ID-token (https://cloud.google.com/docs/authentication/token-types#id)
idToken = "id_token"
)

var tokenTypes = map[string]struct{}{
accessToken: {},
idToken: {},
}

// Config stores the configuration for GCP Client Credentials.
type Config struct {
// Project is the project telemetry is sent to if the gcp.project.id
Expand All @@ -32,6 +47,13 @@ type Config struct {
// https://cloud.google.com/apis/docs/system-parameters
QuotaProject string `mapstructure:"quota_project"`

// TokenType specifies which type of token will be generated.
// default: access_token
TokenType string `mapstructure:"token_type,omitempty"`

// Audience specifies the audience claim used for generating ID token.
Audience string `mapstructure:"audience,omitempty"`

// Scope specifies optional requested permissions.
// See https://datatracker.ietf.org/doc/html/rfc6749#section-3.3
Scopes []string `mapstructure:"scopes,omitempty"`
Expand All @@ -43,6 +65,14 @@ var _ component.Config = (*Config)(nil)

// Validate checks if the extension configuration is valid.
func (cfg *Config) Validate() error {
if _, ok := tokenTypes[cfg.TokenType]; !ok {
return errors.New("invalid token_type")
}

if cfg.TokenType == idToken && cfg.Audience == "" {
return errors.New("audience must be specified when using the id_token token_type")
}

return nil
}

Expand All @@ -56,6 +86,7 @@ var defaultScopes = []string{

func CreateDefaultConfig() component.Config {
return &Config{
Scopes: defaultScopes,
Scopes: defaultScopes,
TokenType: accessToken,
}
}
58 changes: 58 additions & 0 deletions extension/googleclientauthextension/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package googleclientauthextension // import "github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension"

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestConfig_Validate_ValidAccessToken(t *testing.T) {
cfg := &Config{
TokenType: accessToken,
}

err := cfg.Validate()
assert.NoError(t, err)
}

func TestConfig_Validate_ValidIDToken(t *testing.T) {
cfg := &Config{
TokenType: idToken,
Audience: "audience",
}

err := cfg.Validate()
assert.NoError(t, err)
}

func TestConfig_Validate_MissingAudience(t *testing.T) {
cfg := &Config{
TokenType: idToken,
}

err := cfg.Validate()
assert.Error(t, err)
}

func TestConfig_Validate_Invalid(t *testing.T) {
cfg := &Config{
TokenType: "invalid",
}

err := cfg.Validate()
assert.Error(t, err)
}
21 changes: 20 additions & 1 deletion extension/googleclientauthextension/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import (

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/extension"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/idtoken"
"google.golang.org/grpc/credentials/oauth"
)

Expand Down Expand Up @@ -54,10 +56,27 @@ func (ca *clientAuthenticator) Start(ctx context.Context, _ component.Host) erro
if config.QuotaProject == "" {
config.QuotaProject = quotaProjectFromCreds(creds)
}
ca.TokenSource = &oauth.TokenSource{TokenSource: creds.TokenSource}
source, err := ca.newTokenSource(ctx, creds)
if err != nil {
return err
}
ca.TokenSource = &oauth.TokenSource{TokenSource: source}
return nil
}

func (ca *clientAuthenticator) newTokenSource(ctx context.Context, creds *google.Credentials) (oauth2.TokenSource, error) {
switch ca.config.TokenType {
case idToken:
return idtoken.NewTokenSource(
ctx,
ca.config.Audience,
idtoken.WithCredentialsJSON(creds.JSON),
)
default:
return creds.TokenSource, nil
}
}

func (ca *clientAuthenticator) Shutdown(ctx context.Context) error {
return nil
}
Expand Down
8 changes: 8 additions & 0 deletions extension/googleclientauthextension/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
go.opentelemetry.io/collector/component v0.99.0
go.opentelemetry.io/collector/extension v0.94.0
golang.org/x/oauth2 v0.18.0
google.golang.org/api v0.162.0
google.golang.org/grpc v1.63.2
)

Expand All @@ -16,11 +17,15 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/knadh/koanf/maps v0.1.1 // indirect
github.com/knadh/koanf/providers/confmap v0.1.0 // indirect
github.com/knadh/koanf/v2 v2.1.1 // indirect
Expand All @@ -31,9 +36,11 @@ require (
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.52.3 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/collector/config/configtelemetry v0.99.0 // indirect
go.opentelemetry.io/collector/confmap v0.99.0 // indirect
go.opentelemetry.io/collector/pdata v1.6.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect
go.opentelemetry.io/otel v1.25.0 // indirect
go.opentelemetry.io/otel/exporters/prometheus v0.47.0 // indirect
go.opentelemetry.io/otel/metric v1.25.0 // indirect
Expand All @@ -42,6 +49,7 @@ require (
go.opentelemetry.io/otel/trace v1.25.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
Expand Down
Loading

0 comments on commit 51ae889

Please sign in to comment.