...

Source file src/golang.org/x/oauth2/google/default.go

Documentation: golang.org/x/oauth2/google

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package google
     6  
     7  import (
     8  	"context"
     9  	"encoding/json"
    10  	"fmt"
    11  	"net/http"
    12  	"os"
    13  	"path/filepath"
    14  	"runtime"
    15  	"sync"
    16  	"time"
    17  
    18  	"cloud.google.com/go/compute/metadata"
    19  	"golang.org/x/oauth2"
    20  	"golang.org/x/oauth2/authhandler"
    21  )
    22  
    23  const (
    24  	adcSetupURL           = "https://cloud.google.com/docs/authentication/external/set-up-adc"
    25  	defaultUniverseDomain = "googleapis.com"
    26  )
    27  
    28  // Credentials holds Google credentials, including "Application Default Credentials".
    29  // For more details, see:
    30  // https://developers.google.com/accounts/docs/application-default-credentials
    31  // Credentials from external accounts (workload identity federation) are used to
    32  // identify a particular application from an on-prem or non-Google Cloud platform
    33  // including Amazon Web Services (AWS), Microsoft Azure or any identity provider
    34  // that supports OpenID Connect (OIDC).
    35  type Credentials struct {
    36  	ProjectID   string // may be empty
    37  	TokenSource oauth2.TokenSource
    38  
    39  	// JSON contains the raw bytes from a JSON credentials file.
    40  	// This field may be nil if authentication is provided by the
    41  	// environment and not with a credentials file, e.g. when code is
    42  	// running on Google Cloud Platform.
    43  	JSON []byte
    44  
    45  	// UniverseDomainProvider returns the default service domain for a given
    46  	// Cloud universe. Optional.
    47  	//
    48  	// On GCE, UniverseDomainProvider should return the universe domain value
    49  	// from Google Compute Engine (GCE)'s metadata server. See also [The attached service
    50  	// account](https://cloud.google.com/docs/authentication/application-default-credentials#attached-sa).
    51  	// If the GCE metadata server returns a 404 error, the default universe
    52  	// domain value should be returned. If the GCE metadata server returns an
    53  	// error other than 404, the error should be returned.
    54  	UniverseDomainProvider func() (string, error)
    55  
    56  	udMu sync.Mutex // guards universeDomain
    57  	// universeDomain is the default service domain for a given Cloud universe.
    58  	universeDomain string
    59  }
    60  
    61  // UniverseDomain returns the default service domain for a given Cloud universe.
    62  //
    63  // The default value is "googleapis.com".
    64  //
    65  // Deprecated: Use instead (*Credentials).GetUniverseDomain(), which supports
    66  // obtaining the universe domain when authenticating via the GCE metadata server.
    67  // Unlike GetUniverseDomain, this method, UniverseDomain, will always return the
    68  // default value when authenticating via the GCE metadata server.
    69  // See also [The attached service account](https://cloud.google.com/docs/authentication/application-default-credentials#attached-sa).
    70  func (c *Credentials) UniverseDomain() string {
    71  	if c.universeDomain == "" {
    72  		return defaultUniverseDomain
    73  	}
    74  	return c.universeDomain
    75  }
    76  
    77  // GetUniverseDomain returns the default service domain for a given Cloud
    78  // universe. If present, UniverseDomainProvider will be invoked and its return
    79  // value will be cached.
    80  //
    81  // The default value is "googleapis.com".
    82  func (c *Credentials) GetUniverseDomain() (string, error) {
    83  	c.udMu.Lock()
    84  	defer c.udMu.Unlock()
    85  	if c.universeDomain == "" && c.UniverseDomainProvider != nil {
    86  		// On Google Compute Engine, an App Engine standard second generation
    87  		// runtime, or App Engine flexible, use an externally provided function
    88  		// to request the universe domain from the metadata server.
    89  		ud, err := c.UniverseDomainProvider()
    90  		if err != nil {
    91  			return "", err
    92  		}
    93  		c.universeDomain = ud
    94  	}
    95  	// If no UniverseDomainProvider (meaning not on Google Compute Engine), or
    96  	// in case of any (non-error) empty return value from
    97  	// UniverseDomainProvider, set the default universe domain.
    98  	if c.universeDomain == "" {
    99  		c.universeDomain = defaultUniverseDomain
   100  	}
   101  	return c.universeDomain, nil
   102  }
   103  
   104  // DefaultCredentials is the old name of Credentials.
   105  //
   106  // Deprecated: use Credentials instead.
   107  type DefaultCredentials = Credentials
   108  
   109  // CredentialsParams holds user supplied parameters that are used together
   110  // with a credentials file for building a Credentials object.
   111  type CredentialsParams struct {
   112  	// Scopes is the list OAuth scopes. Required.
   113  	// Example: https://www.googleapis.com/auth/cloud-platform
   114  	Scopes []string
   115  
   116  	// Subject is the user email used for domain wide delegation (see
   117  	// https://developers.google.com/identity/protocols/oauth2/service-account#delegatingauthority).
   118  	// Optional.
   119  	Subject string
   120  
   121  	// AuthHandler is the AuthorizationHandler used for 3-legged OAuth flow. Required for 3LO flow.
   122  	AuthHandler authhandler.AuthorizationHandler
   123  
   124  	// State is a unique string used with AuthHandler. Required for 3LO flow.
   125  	State string
   126  
   127  	// PKCE is used to support PKCE flow. Optional for 3LO flow.
   128  	PKCE *authhandler.PKCEParams
   129  
   130  	// The OAuth2 TokenURL default override. This value overrides the default TokenURL,
   131  	// unless explicitly specified by the credentials config file. Optional.
   132  	TokenURL string
   133  
   134  	// EarlyTokenRefresh is the amount of time before a token expires that a new
   135  	// token will be preemptively fetched. If unset the default value is 10
   136  	// seconds.
   137  	//
   138  	// Note: This option is currently only respected when using credentials
   139  	// fetched from the GCE metadata server.
   140  	EarlyTokenRefresh time.Duration
   141  
   142  	// UniverseDomain is the default service domain for a given Cloud universe.
   143  	// Only supported in authentication flows that support universe domains.
   144  	// This value takes precedence over a universe domain explicitly specified
   145  	// in a credentials config file or by the GCE metadata server. Optional.
   146  	UniverseDomain string
   147  }
   148  
   149  func (params CredentialsParams) deepCopy() CredentialsParams {
   150  	paramsCopy := params
   151  	paramsCopy.Scopes = make([]string, len(params.Scopes))
   152  	copy(paramsCopy.Scopes, params.Scopes)
   153  	return paramsCopy
   154  }
   155  
   156  // DefaultClient returns an HTTP Client that uses the
   157  // DefaultTokenSource to obtain authentication credentials.
   158  func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) {
   159  	ts, err := DefaultTokenSource(ctx, scope...)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  	return oauth2.NewClient(ctx, ts), nil
   164  }
   165  
   166  // DefaultTokenSource returns the token source for
   167  // "Application Default Credentials".
   168  // It is a shortcut for FindDefaultCredentials(ctx, scope).TokenSource.
   169  func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) {
   170  	creds, err := FindDefaultCredentials(ctx, scope...)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  	return creds.TokenSource, nil
   175  }
   176  
   177  // FindDefaultCredentialsWithParams searches for "Application Default Credentials".
   178  //
   179  // It looks for credentials in the following places,
   180  // preferring the first location found:
   181  //
   182  //  1. A JSON file whose path is specified by the
   183  //     GOOGLE_APPLICATION_CREDENTIALS environment variable.
   184  //     For workload identity federation, refer to
   185  //     https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation on
   186  //     how to generate the JSON configuration file for on-prem/non-Google cloud
   187  //     platforms.
   188  //  2. A JSON file in a location known to the gcloud command-line tool.
   189  //     On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
   190  //     On other systems, $HOME/.config/gcloud/application_default_credentials.json.
   191  //  3. On Google Compute Engine, Google App Engine standard second generation runtimes
   192  //     (>= Go 1.11), and Google App Engine flexible environment, it fetches
   193  //     credentials from the metadata server.
   194  func FindDefaultCredentialsWithParams(ctx context.Context, params CredentialsParams) (*Credentials, error) {
   195  	// Make defensive copy of the slices in params.
   196  	params = params.deepCopy()
   197  
   198  	// First, try the environment variable.
   199  	const envVar = "GOOGLE_APPLICATION_CREDENTIALS"
   200  	if filename := os.Getenv(envVar); filename != "" {
   201  		creds, err := readCredentialsFile(ctx, filename, params)
   202  		if err != nil {
   203  			return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err)
   204  		}
   205  		return creds, nil
   206  	}
   207  
   208  	// Second, try a well-known file.
   209  	filename := wellKnownFile()
   210  	if b, err := os.ReadFile(filename); err == nil {
   211  		return CredentialsFromJSONWithParams(ctx, b, params)
   212  	}
   213  
   214  	// Third, if we're on Google Compute Engine, an App Engine standard second generation runtime,
   215  	// or App Engine flexible, use the metadata server.
   216  	if metadata.OnGCE() {
   217  		id, _ := metadata.ProjectID()
   218  		universeDomainProvider := func() (string, error) {
   219  			universeDomain, err := metadata.Get("universe/universe_domain")
   220  			if err != nil {
   221  				if _, ok := err.(metadata.NotDefinedError); ok {
   222  					// http.StatusNotFound (404)
   223  					return defaultUniverseDomain, nil
   224  				} else {
   225  					return "", err
   226  				}
   227  			}
   228  			return universeDomain, nil
   229  		}
   230  		return &Credentials{
   231  			ProjectID:              id,
   232  			TokenSource:            computeTokenSource("", params.EarlyTokenRefresh, params.Scopes...),
   233  			UniverseDomainProvider: universeDomainProvider,
   234  			universeDomain:         params.UniverseDomain,
   235  		}, nil
   236  	}
   237  
   238  	// None are found; return helpful error.
   239  	return nil, fmt.Errorf("google: could not find default credentials. See %v for more information", adcSetupURL)
   240  }
   241  
   242  // FindDefaultCredentials invokes FindDefaultCredentialsWithParams with the specified scopes.
   243  func FindDefaultCredentials(ctx context.Context, scopes ...string) (*Credentials, error) {
   244  	var params CredentialsParams
   245  	params.Scopes = scopes
   246  	return FindDefaultCredentialsWithParams(ctx, params)
   247  }
   248  
   249  // CredentialsFromJSONWithParams obtains Google credentials from a JSON value. The JSON can
   250  // represent either a Google Developers Console client_credentials.json file (as in ConfigFromJSON),
   251  // a Google Developers service account key file, a gcloud user credentials file (a.k.a. refresh
   252  // token JSON), or the JSON configuration file for workload identity federation in non-Google cloud
   253  // platforms (see https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation).
   254  func CredentialsFromJSONWithParams(ctx context.Context, jsonData []byte, params CredentialsParams) (*Credentials, error) {
   255  	// Make defensive copy of the slices in params.
   256  	params = params.deepCopy()
   257  
   258  	// First, attempt to parse jsonData as a Google Developers Console client_credentials.json.
   259  	config, _ := ConfigFromJSON(jsonData, params.Scopes...)
   260  	if config != nil {
   261  		return &Credentials{
   262  			ProjectID:   "",
   263  			TokenSource: authhandler.TokenSourceWithPKCE(ctx, config, params.State, params.AuthHandler, params.PKCE),
   264  			JSON:        jsonData,
   265  		}, nil
   266  	}
   267  
   268  	// Otherwise, parse jsonData as one of the other supported credentials files.
   269  	var f credentialsFile
   270  	if err := json.Unmarshal(jsonData, &f); err != nil {
   271  		return nil, err
   272  	}
   273  
   274  	universeDomain := f.UniverseDomain
   275  	if params.UniverseDomain != "" {
   276  		universeDomain = params.UniverseDomain
   277  	}
   278  	// Authorized user credentials are only supported in the googleapis.com universe.
   279  	if f.Type == userCredentialsKey {
   280  		universeDomain = defaultUniverseDomain
   281  	}
   282  
   283  	ts, err := f.tokenSource(ctx, params)
   284  	if err != nil {
   285  		return nil, err
   286  	}
   287  	ts = newErrWrappingTokenSource(ts)
   288  	return &Credentials{
   289  		ProjectID:      f.ProjectID,
   290  		TokenSource:    ts,
   291  		JSON:           jsonData,
   292  		universeDomain: universeDomain,
   293  	}, nil
   294  }
   295  
   296  // CredentialsFromJSON invokes CredentialsFromJSONWithParams with the specified scopes.
   297  func CredentialsFromJSON(ctx context.Context, jsonData []byte, scopes ...string) (*Credentials, error) {
   298  	var params CredentialsParams
   299  	params.Scopes = scopes
   300  	return CredentialsFromJSONWithParams(ctx, jsonData, params)
   301  }
   302  
   303  func wellKnownFile() string {
   304  	const f = "application_default_credentials.json"
   305  	if runtime.GOOS == "windows" {
   306  		return filepath.Join(os.Getenv("APPDATA"), "gcloud", f)
   307  	}
   308  	return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f)
   309  }
   310  
   311  func readCredentialsFile(ctx context.Context, filename string, params CredentialsParams) (*Credentials, error) {
   312  	b, err := os.ReadFile(filename)
   313  	if err != nil {
   314  		return nil, err
   315  	}
   316  	return CredentialsFromJSONWithParams(ctx, b, params)
   317  }
   318  

View as plain text