Copyright 2015 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

package google

import (
	
	
	
	
	
	
	
	
	
	
	
	
	

	
)

type sdkCredentials struct {
	Data []struct {
		Credential struct {
			ClientID     string     `json:"client_id"`
			ClientSecret string     `json:"client_secret"`
			AccessToken  string     `json:"access_token"`
			RefreshToken string     `json:"refresh_token"`
			TokenExpiry  *time.Time `json:"token_expiry"`
		} `json:"credential"`
		Key struct {
			Account string `json:"account"`
			Scope   string `json:"scope"`
		} `json:"key"`
	}
}
An SDKConfig provides access to tokens from an account already authorized via the Google Cloud SDK.
NewSDKConfig creates an SDKConfig for the given Google Cloud SDK account. If account is empty, the account currently active in Google Cloud SDK properties is used. Google Cloud SDK credentials must be created by running `gcloud auth` before using this function. The Google Cloud SDK is available at https://cloud.google.com/sdk/.
func ( string) (*SDKConfig, error) {
	,  := sdkConfigPath()
	if  != nil {
		return nil, fmt.Errorf("oauth2/google: error getting SDK config path: %v", )
	}
	 := filepath.Join(, "credentials")
	,  := os.Open()
	if  != nil {
		return nil, fmt.Errorf("oauth2/google: failed to load SDK credentials: %v", )
	}
	defer .Close()

	var  sdkCredentials
	if  := json.NewDecoder().Decode(&);  != nil {
		return nil, fmt.Errorf("oauth2/google: failed to decode SDK credentials from %q: %v", , )
	}
	if len(.Data) == 0 {
		return nil, fmt.Errorf("oauth2/google: no credentials found in %q, run `gcloud auth login` to create one", )
	}
	if  == "" {
		 := filepath.Join(, "properties")
		,  := os.Open()
		if  != nil {
			return nil, fmt.Errorf("oauth2/google: failed to load SDK properties: %v", )
		}
		defer .Close()
		,  := parseINI()
		if  != nil {
			return nil, fmt.Errorf("oauth2/google: failed to parse SDK properties %q: %v", , )
		}
		,  := ["core"]
		if ! {
			return nil, fmt.Errorf("oauth2/google: failed to find [core] section in %v", )
		}
		,  := ["account"]
		if ! {
			return nil, fmt.Errorf("oauth2/google: failed to find %q attribute in %v", "account", )
		}
		 = 
	}

	for ,  := range .Data {
		if  == "" || .Key.Account ==  {
			if .Credential.AccessToken == "" && .Credential.RefreshToken == "" {
				return nil, fmt.Errorf("oauth2/google: no token available for account %q", )
			}
			var  time.Time
			if .Credential.TokenExpiry != nil {
				 = *.Credential.TokenExpiry
			}
			return &SDKConfig{
				conf: oauth2.Config{
					ClientID:     .Credential.ClientID,
					ClientSecret: .Credential.ClientSecret,
					Scopes:       strings.Split(.Key.Scope, " "),
					Endpoint:     Endpoint,
					RedirectURL:  "oob",
				},
				initialToken: &oauth2.Token{
					AccessToken:  .Credential.AccessToken,
					RefreshToken: .Credential.RefreshToken,
					Expiry:       ,
				},
			}, nil
		}
	}
	return nil, fmt.Errorf("oauth2/google: no such credentials for account %q", )
}
Client returns an HTTP client using Google Cloud SDK credentials to authorize requests. The token will auto-refresh as necessary. The underlying http.RoundTripper will be obtained using the provided context. The returned client and its Transport should not be modified.
TokenSource returns an oauth2.TokenSource that retrieve tokens from Google Cloud SDK credentials using the provided context. It will returns the current access token stored in the credentials, and refresh it when it expires, but it won't update the credentials with the new access token.
Scopes are the OAuth 2.0 scopes the current account is authorized for.
func ( *SDKConfig) () []string {
	return .conf.Scopes
}

func ( io.Reader) (map[string]map[string]string, error) {
	 := map[string]map[string]string{
		"": {}, // root section
	}
	 := bufio.NewScanner()
	 := ""
	for .Scan() {
		 := strings.TrimSpace(.Text())
comment.
			continue
		}
		if strings.HasPrefix(, "[") && strings.HasSuffix(, "]") {
			 = strings.TrimSpace([1 : len()-1])
			[] = map[string]string{}
			continue
		}
		 := strings.SplitN(, "=", 2)
		if len() == 2 && [0] != "" {
			[][strings.TrimSpace([0])] = strings.TrimSpace([1])
		}
	}
	if  := .Err();  != nil {
		return nil, fmt.Errorf("error scanning ini: %v", )
	}
	return , nil
}
sdkConfigPath tries to guess where the gcloud config is located. It can be overridden during tests.
var sdkConfigPath = func() (string, error) {
	if runtime.GOOS == "windows" {
		return filepath.Join(os.Getenv("APPDATA"), "gcloud"), nil
	}
	 := guessUnixHomeDir()
	if  == "" {
		return "", errors.New("unable to get current user home directory: os/user lookup failed; $HOME is empty")
	}
	return filepath.Join(, ".config", "gcloud"), nil
}

Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470
	if  := os.Getenv("HOME");  != "" {
		return 
Else, fall back to user.Current:
	if ,  := user.Current();  == nil {
		return .HomeDir
	}
	return ""