Skip to content

Commit

Permalink
feat(cloudclient): support id_token with authorized_user type
Browse files Browse the repository at this point in the history
  • Loading branch information
liufuyang committed Sep 12, 2022
1 parent f448021 commit 0e7c93a
Showing 1 changed file with 50 additions and 1 deletion.
51 changes: 50 additions & 1 deletion cloudclient/dial.go
Expand Up @@ -8,6 +8,10 @@ import (
"strings"
"time"

"go.einride.tech/cloudrunner/cloudzap"
"go.uber.org/zap"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/idtoken"
"google.golang.org/api/option"
"google.golang.org/grpc"
Expand All @@ -21,8 +25,28 @@ func DialService(ctx context.Context, target string, opts ...grpc.DialOption) (*
audience := "https://" + trimPort(target)
idTokenSource, err := idtoken.NewTokenSource(ctx, audience, option.WithAudiences(audience))
if err != nil {
return nil, fmt.Errorf("dial %s: %w", target, err)
// Google's idtoken package does not support credential type other than `service_account`.
// This blocks local development with using `impersonated_service_account` type credentials. If that happens,
// we work it around by using our Application Default Credentials (which is impersonated already) to fetch
// an id_token on the fly.
// This however still blocks `authorized_user` type of credentials passing through.
// Related issue page: https://github.com/googleapis/google-api-go-client/issues/873
if err.Error() != `idtoken: credential must be service_account, found "impersonated_service_account"` {
return nil, err
}
logger, ok := cloudzap.GetLogger(ctx)
if !ok {
logger = zap.NewNop()
}
logger.Sugar().Warnf("%s. Using Application Default Credentials to fetch id_token again. "+
"This should never happen in prod env.", err.Error())
gts, err := google.DefaultTokenSource(ctx)
if err != nil {
return nil, err
}
idTokenSource = oauth2.ReuseTokenSource(nil, &idTokenSourceWrapper{TokenSource: gts})
}

systemCertPool, err := x509.SystemCertPool()
if err != nil {
return nil, fmt.Errorf("dial %s: %w", target, err)
Expand Down Expand Up @@ -62,3 +86,28 @@ func withDefaultPort(target string, port int) string {
}
return target
}

// idTokenSourceWrapper is an oauth2.TokenSource wrapper used for getting id_token for local development using
// `authorized_user` type credentials
// It takes the id_token from TokenSource and passes that on as a bearer token.
type idTokenSourceWrapper struct {
TokenSource oauth2.TokenSource
}

func (s *idTokenSourceWrapper) Token() (*oauth2.Token, error) {
token, err := s.TokenSource.Token()
if err != nil {
return nil, err
}

idToken, ok := token.Extra("id_token").(string)
if !ok {
return nil, fmt.Errorf("token did not contain an id_token")
}

return &oauth2.Token{
AccessToken: idToken,
TokenType: "Bearer",
Expiry: token.Expiry,
}, nil
}

0 comments on commit 0e7c93a

Please sign in to comment.