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