github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/golang.org/x/oauth2/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  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"io/ioutil"
    12  	"net/http"
    13  	"os"
    14  	"path/filepath"
    15  	"runtime"
    16  
    17  	"golang.org/x/net/context"
    18  	"golang.org/x/oauth2"
    19  	"golang.org/x/oauth2/jwt"
    20  	"google.golang.org/cloud/compute/metadata"
    21  )
    22  
    23  // DefaultClient returns an HTTP Client that uses the
    24  // DefaultTokenSource to obtain authentication credentials.
    25  //
    26  // This client should be used when developing services
    27  // that run on Google App Engine or Google Compute Engine
    28  // and use "Application Default Credentials."
    29  //
    30  // For more details, see:
    31  // https://developers.google.com/accounts/docs/application-default-credentials
    32  //
    33  func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) {
    34  	ts, err := DefaultTokenSource(ctx, scope...)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	return oauth2.NewClient(ctx, ts), nil
    39  }
    40  
    41  // DefaultTokenSource is a token source that uses
    42  // "Application Default Credentials".
    43  //
    44  // It looks for credentials in the following places,
    45  // preferring the first location found:
    46  //
    47  //   1. A JSON file whose path is specified by the
    48  //      GOOGLE_APPLICATION_CREDENTIALS environment variable.
    49  //   2. A JSON file in a location known to the gcloud command-line tool.
    50  //      On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
    51  //      On other systems, $HOME/.config/gcloud/application_default_credentials.json.
    52  //   3. On Google App Engine it uses the appengine.AccessToken function.
    53  //   4. On Google Compute Engine and Google App Engine Managed VMs, it fetches
    54  //      credentials from the metadata server.
    55  //      (In this final case any provided scopes are ignored.)
    56  //
    57  // For more details, see:
    58  // https://developers.google.com/accounts/docs/application-default-credentials
    59  //
    60  func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) {
    61  	// First, try the environment variable.
    62  	const envVar = "GOOGLE_APPLICATION_CREDENTIALS"
    63  	if filename := os.Getenv(envVar); filename != "" {
    64  		ts, err := tokenSourceFromFile(ctx, filename, scope)
    65  		if err != nil {
    66  			return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err)
    67  		}
    68  		return ts, nil
    69  	}
    70  
    71  	// Second, try a well-known file.
    72  	filename := wellKnownFile()
    73  	_, err := os.Stat(filename)
    74  	if err == nil {
    75  		ts, err2 := tokenSourceFromFile(ctx, filename, scope)
    76  		if err2 == nil {
    77  			return ts, nil
    78  		}
    79  		err = err2
    80  	} else if os.IsNotExist(err) {
    81  		err = nil // ignore this error
    82  	}
    83  	if err != nil {
    84  		return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err)
    85  	}
    86  
    87  	// Third, if we're on Google App Engine use those credentials.
    88  	if appengineTokenFunc != nil && !appengineVM {
    89  		return AppEngineTokenSource(ctx, scope...), nil
    90  	}
    91  
    92  	// Fourth, if we're on Google Compute Engine use the metadata server.
    93  	if metadata.OnGCE() {
    94  		return ComputeTokenSource(""), nil
    95  	}
    96  
    97  	// None are found; return helpful error.
    98  	const url = "https://developers.google.com/accounts/docs/application-default-credentials"
    99  	return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url)
   100  }
   101  
   102  func wellKnownFile() string {
   103  	const f = "application_default_credentials.json"
   104  	if runtime.GOOS == "windows" {
   105  		return filepath.Join(os.Getenv("APPDATA"), "gcloud", f)
   106  	}
   107  	return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f)
   108  }
   109  
   110  func tokenSourceFromFile(ctx context.Context, filename string, scopes []string) (oauth2.TokenSource, error) {
   111  	b, err := ioutil.ReadFile(filename)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	var d struct {
   116  		// Common fields
   117  		Type     string
   118  		ClientID string `json:"client_id"`
   119  
   120  		// User Credential fields
   121  		ClientSecret string `json:"client_secret"`
   122  		RefreshToken string `json:"refresh_token"`
   123  
   124  		// Service Account fields
   125  		ClientEmail  string `json:"client_email"`
   126  		PrivateKeyID string `json:"private_key_id"`
   127  		PrivateKey   string `json:"private_key"`
   128  	}
   129  	if err := json.Unmarshal(b, &d); err != nil {
   130  		return nil, err
   131  	}
   132  	switch d.Type {
   133  	case "authorized_user":
   134  		cfg := &oauth2.Config{
   135  			ClientID:     d.ClientID,
   136  			ClientSecret: d.ClientSecret,
   137  			Scopes:       append([]string{}, scopes...), // copy
   138  			Endpoint:     Endpoint,
   139  		}
   140  		tok := &oauth2.Token{RefreshToken: d.RefreshToken}
   141  		return cfg.TokenSource(ctx, tok), nil
   142  	case "service_account":
   143  		cfg := &jwt.Config{
   144  			Email:      d.ClientEmail,
   145  			PrivateKey: []byte(d.PrivateKey),
   146  			Scopes:     append([]string{}, scopes...), // copy
   147  			TokenURL:   JWTTokenURL,
   148  		}
   149  		return cfg.TokenSource(ctx), nil
   150  	case "":
   151  		return nil, errors.New("missing 'type' field in credentials")
   152  	default:
   153  		return nil, fmt.Errorf("unknown credential type: %q", d.Type)
   154  	}
   155  }