Просмотр исходного кода

Updated golang/x/oauth2 dependency, #1710

Torkel Ödegaard 10 лет назад
Родитель
Сommit
b8c378f2f0
36 измененных файлов с 1529 добавлено и 397 удалено
  1. 2 2
      Godeps/Godeps.json
  2. 2 2
      Godeps/_workspace/src/golang.org/x/oauth2/.travis.yml
  3. 25 19
      Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTING.md
  4. 46 0
      Godeps/_workspace/src/golang.org/x/oauth2/README.md
  5. 5 20
      Godeps/_workspace/src/golang.org/x/oauth2/client_appengine.go
  6. 0 27
      Godeps/_workspace/src/golang.org/x/oauth2/example_test.go
  7. 16 0
      Godeps/_workspace/src/golang.org/x/oauth2/facebook/facebook.go
  8. 1 1
      Godeps/_workspace/src/golang.org/x/oauth2/github/github.go
  9. 59 13
      Godeps/_workspace/src/golang.org/x/oauth2/google/appengine.go
  10. 13 0
      Godeps/_workspace/src/golang.org/x/oauth2/google/appengine_hook.go
  11. 0 36
      Godeps/_workspace/src/golang.org/x/oauth2/google/appenginevm.go
  12. 154 0
      Godeps/_workspace/src/golang.org/x/oauth2/google/default.go
  13. 26 8
      Godeps/_workspace/src/golang.org/x/oauth2/google/example_test.go
  14. 77 47
      Godeps/_workspace/src/golang.org/x/oauth2/google/google.go
  15. 67 0
      Godeps/_workspace/src/golang.org/x/oauth2/google/google_test.go
  16. 168 0
      Godeps/_workspace/src/golang.org/x/oauth2/google/sdk.go
  17. 46 0
      Godeps/_workspace/src/golang.org/x/oauth2/google/sdk_test.go
  18. 0 68
      Godeps/_workspace/src/golang.org/x/oauth2/google/source_appengine.go
  19. 122 0
      Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/credentials
  20. 2 0
      Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/properties
  21. 34 2
      Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2.go
  22. 62 0
      Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2_test.go
  23. 1 1
      Godeps/_workspace/src/golang.org/x/oauth2/jws/jws.go
  24. 31 0
      Godeps/_workspace/src/golang.org/x/oauth2/jwt/example_test.go
  25. 23 31
      Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt.go
  26. 22 14
      Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt_test.go
  27. 16 0
      Godeps/_workspace/src/golang.org/x/oauth2/linkedin/linkedin.go
  28. 183 81
      Godeps/_workspace/src/golang.org/x/oauth2/oauth2.go
  29. 183 4
      Godeps/_workspace/src/golang.org/x/oauth2/oauth2_test.go
  30. 16 0
      Godeps/_workspace/src/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go
  31. 22 0
      Godeps/_workspace/src/golang.org/x/oauth2/paypal/paypal.go
  32. 32 15
      Godeps/_workspace/src/golang.org/x/oauth2/token.go
  33. 50 0
      Godeps/_workspace/src/golang.org/x/oauth2/token_test.go
  34. 5 5
      Godeps/_workspace/src/golang.org/x/oauth2/transport_test.go
  35. 16 0
      Godeps/_workspace/src/golang.org/x/oauth2/vk/vk.go
  36. 2 1
      pkg/social/social.go

+ 2 - 2
Godeps/Godeps.json

@@ -1,6 +1,6 @@
 {
 {
 	"ImportPath": "github.com/grafana/grafana",
 	"ImportPath": "github.com/grafana/grafana",
-	"GoVersion": "go1.4.2",
+	"GoVersion": "go1.3",
 	"Packages": [
 	"Packages": [
 		"./pkg/..."
 		"./pkg/..."
 	],
 	],
@@ -68,7 +68,7 @@
 		},
 		},
 		{
 		{
 			"ImportPath": "golang.org/x/oauth2",
 			"ImportPath": "golang.org/x/oauth2",
-			"Rev": "e5909d4679a1926c774c712b343f10b8298687a3"
+			"Rev": "c58fcf0ffc1c772aa2e1ee4894bc19f2649263b2"
 		},
 		},
 		{
 		{
 			"ImportPath": "gopkg.in/bufio.v1",
 			"ImportPath": "gopkg.in/bufio.v1",

+ 2 - 2
Godeps/_workspace/src/golang.org/x/oauth2/.travis.yml

@@ -8,7 +8,7 @@ install:
   - export GOPATH="$HOME/gopath"
   - export GOPATH="$HOME/gopath"
   - mkdir -p "$GOPATH/src/golang.org/x"
   - mkdir -p "$GOPATH/src/golang.org/x"
   - mv "$TRAVIS_BUILD_DIR" "$GOPATH/src/golang.org/x/oauth2"
   - mv "$TRAVIS_BUILD_DIR" "$GOPATH/src/golang.org/x/oauth2"
-  - go get -v -t -d -tags='appengine appenginevm' golang.org/x/oauth2/...
+  - go get -v -t -d golang.org/x/oauth2/...
 
 
 script:
 script:
-  - go test -v -tags='appengine appenginevm' golang.org/x/oauth2/...
+  - go test -v golang.org/x/oauth2/...

+ 25 - 19
Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTING.md

@@ -1,25 +1,31 @@
-# Contributing
+# Contributing to Go
 
 
-We don't use GitHub pull requests but use Gerrit for code reviews,
-similar to the Go project.
+Go is an open source project.
 
 
-1. Sign one of the contributor license agreements below.
-2. `go get golang.org/x/review/git-codereview` to install the code reviewing tool.
-3. Get the package by running `go get -d golang.org/x/oauth2`.
-Make changes and create a change by running `git codereview change <name>`, provide a command message, and use `git codereview mail` to create a Gerrit CL.
-Keep amending to the change and mail as your recieve feedback.
+It is the work of hundreds of contributors. We appreciate your help!
 
 
-For more information about the workflow, see Go's [Contribution Guidelines](https://golang.org/doc/contribute.html).
 
 
-Before we can accept any pull requests
-we have to jump through a couple of legal hurdles,
-primarily a Contributor License Agreement (CLA):
+## Filing issues
 
 
-- **If you are an individual writing original source code**
-  and you're sure you own the intellectual property,
-  then you'll need to sign an [individual CLA](http://code.google.com/legal/individual-cla-v1.0.html).
-- **If you work for a company that wants to allow you to contribute your work**,
-  then you'll need to sign a [corporate CLA](http://code.google.com/legal/corporate-cla-v1.0.html).
+When [filing an issue](https://github.com/golang/oauth2/issues), make sure to answer these five questions:
+
+1. What version of Go are you using (`go version`)?
+2. What operating system and processor architecture are you using?
+3. What did you do?
+4. What did you expect to see?
+5. What did you see instead?
+
+General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
+The gophers there will answer or ask you to file an issue if you've tripped over a bug.
+
+## Contributing code
+
+Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
+before sending patches.
+
+**We do not accept GitHub pull requests**
+(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
+
+Unless otherwise noted, the Go source files are distributed under
+the BSD-style license found in the LICENSE file.
 
 
-You can sign these electronically (just scroll to the bottom).
-After that, we'll be able to accept your pull requests.

+ 46 - 0
Godeps/_workspace/src/golang.org/x/oauth2/README.md

@@ -16,3 +16,49 @@ See godoc for further documentation and examples.
 * [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google)
 * [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google)
 
 
 
 
+## App Engine
+
+In change 96e89be (March 2015) we removed the `oauth2.Context2` type in favor
+of the [`context.Context`](https://golang.org/x/net/context#Context) type from
+the `golang.org/x/net/context` package
+
+This means its no longer possible to use the "Classic App Engine"
+`appengine.Context` type with the `oauth2` package. (You're using
+Classic App Engine if you import the package `"appengine"`.)
+
+To work around this, you may use the new `"google.golang.org/appengine"`
+package. This package has almost the same API as the `"appengine"` package,
+but it can be fetched with `go get` and used on "Managed VMs" and well as
+Classic App Engine.
+
+See the [new `appengine` package's readme](https://github.com/golang/appengine#updating-a-go-app-engine-app)
+for information on updating your app.
+
+If you don't want to update your entire app to use the new App Engine packages,
+you may use both sets of packages in parallel, using only the new packages
+with the `oauth2` package.
+
+	import (
+		"golang.org/x/net/context"
+		"golang.org/x/oauth2"
+		"golang.org/x/oauth2/google"
+		newappengine "google.golang.org/appengine"
+		newurlftech "google.golang.org/urlfetch"
+
+		"appengine"
+	)
+
+	func handler(w http.ResponseWriter, r *http.Request) {
+		var c appengine.Context = appengine.NewContext(r)
+		c.Infof("Logging a message with the old package")
+
+		var ctx context.Context = newappengine.NewContext(r)
+		client := &http.Client{
+			Transport: &oauth2.Transport{
+				Source: google.AppEngineTokenSource(ctx, "scope"),
+				Base:   &newurlfetch.Transport{Context: ctx},
+			},
+		}
+		client.Get("...")
+	}
+

+ 5 - 20
Godeps/_workspace/src/golang.org/x/oauth2/client_appengine.go

@@ -2,38 +2,23 @@
 // Use of this source code is governed by a BSD-style
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 // license that can be found in the LICENSE file.
 
 
-// +build appengine,!appenginevm
+// +build appengine appenginevm
 
 
 // App Engine hooks.
 // App Engine hooks.
 
 
 package oauth2
 package oauth2
 
 
 import (
 import (
-	"log"
 	"net/http"
 	"net/http"
-	"sync"
 
 
-	"appengine"
-	"appengine/urlfetch"
+	"golang.org/x/net/context"
+	"google.golang.org/appengine/urlfetch"
 )
 )
 
 
-var warnOnce sync.Once
-
 func init() {
 func init() {
 	registerContextClientFunc(contextClientAppEngine)
 	registerContextClientFunc(contextClientAppEngine)
 }
 }
 
 
-func contextClientAppEngine(ctx Context) (*http.Client, error) {
-	if actx, ok := ctx.(appengine.Context); ok {
-		return urlfetch.Client(actx), nil
-	}
-	// The user did it wrong. We'll log once (and hope they see it
-	// in dev_appserver), but stil return (nil, nil) in case some
-	// other contextClientFunc hook finds a way to proceed.
-	warnOnce.Do(gaeDoingItWrongHelp)
-	return nil, nil
-}
-
-func gaeDoingItWrongHelp() {
-	log.Printf("WARNING: you attempted to use the oauth2 package without passing a valid appengine.Context or *http.Request as the oauth2.Context. App Engine requires that all service RPCs (including urlfetch) be associated with an *http.Request/appengine.Context.")
+func contextClientAppEngine(ctx context.Context) (*http.Client, error) {
+	return urlfetch.Client(ctx), nil
 }
 }

+ 0 - 27
Godeps/_workspace/src/golang.org/x/oauth2/example_test.go

@@ -7,15 +7,10 @@ package oauth2_test
 import (
 import (
 	"fmt"
 	"fmt"
 	"log"
 	"log"
-	"testing"
 
 
 	"golang.org/x/oauth2"
 	"golang.org/x/oauth2"
 )
 )
 
 
-// TODO(jbd): Remove after Go 1.4.
-// Related to https://codereview.appspot.com/107320046
-func TestA(t *testing.T) {}
-
 func ExampleConfig() {
 func ExampleConfig() {
 	conf := &oauth2.Config{
 	conf := &oauth2.Config{
 		ClientID:     "YOUR_CLIENT_ID",
 		ClientID:     "YOUR_CLIENT_ID",
@@ -48,25 +43,3 @@ func ExampleConfig() {
 	client := conf.Client(oauth2.NoContext, tok)
 	client := conf.Client(oauth2.NoContext, tok)
 	client.Get("...")
 	client.Get("...")
 }
 }
-
-func ExampleJWTConfig() {
-	var initialToken *oauth2.Token // nil means no initial token
-	conf := &oauth2.JWTConfig{
-		Email: "xxx@developer.com",
-		// The contents of your RSA private key or your PEM file
-		// that contains a private key.
-		// If you have a p12 file instead, you
-		// can use `openssl` to export the private key into a pem file.
-		//
-		//    $ openssl pkcs12 -in key.p12 -out key.pem -nodes
-		//
-		// It only supports PEM containers with no passphrase.
-		PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."),
-		Subject:    "user@example.com",
-		TokenURL:   "https://provider.com/o/oauth2/token",
-	}
-	// Initiate an http.Client, the following GET request will be
-	// authorized and authenticated on the behalf of user@example.com.
-	client := conf.Client(oauth2.NoContext, initialToken)
-	client.Get("...")
-}

+ 16 - 0
Godeps/_workspace/src/golang.org/x/oauth2/facebook/facebook.go

@@ -0,0 +1,16 @@
+// Copyright 2015 The oauth2 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 facebook provides constants for using OAuth2 to access Facebook.
+package facebook // import "golang.org/x/oauth2/facebook"
+
+import (
+	"golang.org/x/oauth2"
+)
+
+// Endpoint is Facebook's OAuth 2.0 endpoint.
+var Endpoint = oauth2.Endpoint{
+	AuthURL:  "https://www.facebook.com/dialog/oauth",
+	TokenURL: "https://graph.facebook.com/oauth/access_token",
+}

+ 1 - 1
Godeps/_workspace/src/golang.org/x/oauth2/github/github.go

@@ -3,7 +3,7 @@
 // license that can be found in the LICENSE file.
 // license that can be found in the LICENSE file.
 
 
 // Package github provides constants for using OAuth2 to access Github.
 // Package github provides constants for using OAuth2 to access Github.
-package github
+package github // import "golang.org/x/oauth2/github"
 
 
 import (
 import (
 	"golang.org/x/oauth2"
 	"golang.org/x/oauth2"

+ 59 - 13
Godeps/_workspace/src/golang.org/x/oauth2/google/appengine.go

@@ -2,36 +2,82 @@
 // Use of this source code is governed by a BSD-style
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 // license that can be found in the LICENSE file.
 
 
-// +build appengine,!appenginevm
-
 package google
 package google
 
 
 import (
 import (
+	"sort"
+	"strings"
+	"sync"
 	"time"
 	"time"
 
 
-	"appengine"
-
+	"golang.org/x/net/context"
 	"golang.org/x/oauth2"
 	"golang.org/x/oauth2"
 )
 )
 
 
+// Set at init time by appengine_hook.go. If nil, we're not on App Engine.
+var appengineTokenFunc func(c context.Context, scopes ...string) (token string, expiry time.Time, err error)
+
 // AppEngineTokenSource returns a token source that fetches tokens
 // AppEngineTokenSource returns a token source that fetches tokens
 // issued to the current App Engine application's service account.
 // issued to the current App Engine application's service account.
 // If you are implementing a 3-legged OAuth 2.0 flow on App Engine
 // If you are implementing a 3-legged OAuth 2.0 flow on App Engine
 // that involves user accounts, see oauth2.Config instead.
 // that involves user accounts, see oauth2.Config instead.
 //
 //
-// You are required to provide a valid appengine.Context as context.
-func AppEngineTokenSource(ctx appengine.Context, scope ...string) oauth2.TokenSource {
+// The provided context must have come from appengine.NewContext.
+func AppEngineTokenSource(ctx context.Context, scope ...string) oauth2.TokenSource {
+	if appengineTokenFunc == nil {
+		panic("google: AppEngineTokenSource can only be used on App Engine.")
+	}
+	scopes := append([]string{}, scope...)
+	sort.Strings(scopes)
 	return &appEngineTokenSource{
 	return &appEngineTokenSource{
-		ctx:         ctx,
-		scopes:      scope,
-		fetcherFunc: aeFetcherFunc,
+		ctx:    ctx,
+		scopes: scopes,
+		key:    strings.Join(scopes, " "),
 	}
 	}
 }
 }
 
 
-var aeFetcherFunc = func(ctx oauth2.Context, scope ...string) (string, time.Time, error) {
-	c, ok := ctx.(appengine.Context)
+// aeTokens helps the fetched tokens to be reused until their expiration.
+var (
+	aeTokensMu sync.Mutex
+	aeTokens   = make(map[string]*tokenLock) // key is space-separated scopes
+)
+
+type tokenLock struct {
+	mu sync.Mutex // guards t; held while fetching or updating t
+	t  *oauth2.Token
+}
+
+type appEngineTokenSource struct {
+	ctx    context.Context
+	scopes []string
+	key    string // to aeTokens map; space-separated scopes
+}
+
+func (ts *appEngineTokenSource) Token() (*oauth2.Token, error) {
+	if appengineTokenFunc == nil {
+		panic("google: AppEngineTokenSource can only be used on App Engine.")
+	}
+
+	aeTokensMu.Lock()
+	tok, ok := aeTokens[ts.key]
 	if !ok {
 	if !ok {
-		return "", time.Time{}, errInvalidContext
+		tok = &tokenLock{}
+		aeTokens[ts.key] = tok
+	}
+	aeTokensMu.Unlock()
+
+	tok.mu.Lock()
+	defer tok.mu.Unlock()
+	if tok.t.Valid() {
+		return tok.t, nil
+	}
+	access, exp, err := appengineTokenFunc(ts.ctx, ts.scopes...)
+	if err != nil {
+		return nil, err
+	}
+	tok.t = &oauth2.Token{
+		AccessToken: access,
+		Expiry:      exp,
 	}
 	}
-	return appengine.AccessToken(c, scope...)
+	return tok.t, nil
 }
 }

+ 13 - 0
Godeps/_workspace/src/golang.org/x/oauth2/google/appengine_hook.go

@@ -0,0 +1,13 @@
+// Copyright 2015 The oauth2 Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build appengine appenginevm
+
+package google
+
+import "google.golang.org/appengine"
+
+func init() {
+	appengineTokenFunc = appengine.AccessToken
+}

+ 0 - 36
Godeps/_workspace/src/golang.org/x/oauth2/google/appenginevm.go

@@ -1,36 +0,0 @@
-// Copyright 2014 The oauth2 Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build appenginevm !appengine
-
-package google
-
-import (
-	"time"
-
-	"golang.org/x/oauth2"
-	"google.golang.org/appengine"
-)
-
-// AppEngineTokenSource returns a token source that fetches tokens
-// issued to the current App Engine application's service account.
-// If you are implementing a 3-legged OAuth 2.0 flow on App Engine
-// that involves user accounts, see oauth2.Config instead.
-//
-// You are required to provide a valid appengine.Context as context.
-func AppEngineTokenSource(ctx appengine.Context, scope ...string) oauth2.TokenSource {
-	return &appEngineTokenSource{
-		ctx:         ctx,
-		scopes:      scope,
-		fetcherFunc: aeVMFetcherFunc,
-	}
-}
-
-var aeVMFetcherFunc = func(ctx oauth2.Context, scope ...string) (string, time.Time, error) {
-	c, ok := ctx.(appengine.Context)
-	if !ok {
-		return "", time.Time{}, errInvalidContext
-	}
-	return appengine.AccessToken(c, scope...)
-}

+ 154 - 0
Godeps/_workspace/src/golang.org/x/oauth2/google/default.go

@@ -0,0 +1,154 @@
+// Copyright 2015 The oauth2 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 (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"path/filepath"
+	"runtime"
+
+	"golang.org/x/net/context"
+	"golang.org/x/oauth2"
+	"golang.org/x/oauth2/jwt"
+	"google.golang.org/cloud/compute/metadata"
+)
+
+// DefaultClient returns an HTTP Client that uses the
+// DefaultTokenSource to obtain authentication credentials.
+//
+// This client should be used when developing services
+// that run on Google App Engine or Google Compute Engine
+// and use "Application Default Credentials."
+//
+// For more details, see:
+// https://developers.google.com/accounts/application-default-credentials
+//
+func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) {
+	ts, err := DefaultTokenSource(ctx, scope...)
+	if err != nil {
+		return nil, err
+	}
+	return oauth2.NewClient(ctx, ts), nil
+}
+
+// DefaultTokenSource is a token source that uses
+// "Application Default Credentials".
+//
+// It looks for credentials in the following places,
+// preferring the first location found:
+//
+//   1. A JSON file whose path is specified by the
+//      GOOGLE_APPLICATION_CREDENTIALS environment variable.
+//   2. A JSON file in a location known to the gcloud command-line tool.
+//      On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
+//      On other systems, $HOME/.config/gcloud/application_default_credentials.json.
+//   3. On Google App Engine it uses the appengine.AccessToken function.
+//   4. On Google Compute Engine, it fetches credentials from the metadata server.
+//      (In this final case any provided scopes are ignored.)
+//
+// For more details, see:
+// https://developers.google.com/accounts/application-default-credentials
+//
+func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) {
+	// First, try the environment variable.
+	const envVar = "GOOGLE_APPLICATION_CREDENTIALS"
+	if filename := os.Getenv(envVar); filename != "" {
+		ts, err := tokenSourceFromFile(ctx, filename, scope)
+		if err != nil {
+			return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err)
+		}
+		return ts, nil
+	}
+
+	// Second, try a well-known file.
+	filename := wellKnownFile()
+	_, err := os.Stat(filename)
+	if err == nil {
+		ts, err2 := tokenSourceFromFile(ctx, filename, scope)
+		if err2 == nil {
+			return ts, nil
+		}
+		err = err2
+	} else if os.IsNotExist(err) {
+		err = nil // ignore this error
+	}
+	if err != nil {
+		return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err)
+	}
+
+	// Third, if we're on Google App Engine use those credentials.
+	if appengineTokenFunc != nil {
+		return AppEngineTokenSource(ctx, scope...), nil
+	}
+
+	// Fourth, if we're on Google Compute Engine use the metadata server.
+	if metadata.OnGCE() {
+		return ComputeTokenSource(""), nil
+	}
+
+	// None are found; return helpful error.
+	const url = "https://developers.google.com/accounts/application-default-credentials"
+	return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url)
+}
+
+func wellKnownFile() string {
+	const f = "application_default_credentials.json"
+	if runtime.GOOS == "windows" {
+		return filepath.Join(os.Getenv("APPDATA"), "gcloud", f)
+	}
+	return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f)
+}
+
+func tokenSourceFromFile(ctx context.Context, filename string, scopes []string) (oauth2.TokenSource, error) {
+	b, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return nil, err
+	}
+	var d struct {
+		// Common fields
+		Type     string
+		ClientID string `json:"client_id"`
+
+		// User Credential fields
+		ClientSecret string `json:"client_secret"`
+		RefreshToken string `json:"refresh_token"`
+
+		// Service Account fields
+		ClientEmail  string `json:"client_email"`
+		PrivateKeyID string `json:"private_key_id"`
+		PrivateKey   string `json:"private_key"`
+	}
+	if err := json.Unmarshal(b, &d); err != nil {
+		return nil, err
+	}
+	switch d.Type {
+	case "authorized_user":
+		cfg := &oauth2.Config{
+			ClientID:     d.ClientID,
+			ClientSecret: d.ClientSecret,
+			Scopes:       append([]string{}, scopes...), // copy
+			Endpoint:     Endpoint,
+		}
+		tok := &oauth2.Token{RefreshToken: d.RefreshToken}
+		return cfg.TokenSource(ctx, tok), nil
+	case "service_account":
+		cfg := &jwt.Config{
+			Email:      d.ClientEmail,
+			PrivateKey: []byte(d.PrivateKey),
+			Scopes:     append([]string{}, scopes...), // copy
+			TokenURL:   JWTTokenURL,
+		}
+		return cfg.TokenSource(ctx), nil
+	case "":
+		return nil, errors.New("missing 'type' field in credentials")
+	default:
+		return nil, fmt.Errorf("unknown credential type: %q", d.Type)
+	}
+}

+ 26 - 8
Godeps/_workspace/src/golang.org/x/oauth2/google/example_test.go

@@ -11,17 +11,22 @@ import (
 	"io/ioutil"
 	"io/ioutil"
 	"log"
 	"log"
 	"net/http"
 	"net/http"
-	"testing"
 
 
 	"golang.org/x/oauth2"
 	"golang.org/x/oauth2"
 	"golang.org/x/oauth2/google"
 	"golang.org/x/oauth2/google"
+	"golang.org/x/oauth2/jwt"
 	"google.golang.org/appengine"
 	"google.golang.org/appengine"
 	"google.golang.org/appengine/urlfetch"
 	"google.golang.org/appengine/urlfetch"
 )
 )
 
 
-// Remove after Go 1.4.
-// Related to https://codereview.appspot.com/107320046
-func TestA(t *testing.T) {}
+func ExampleDefaultClient() {
+	client, err := google.DefaultClient(oauth2.NoContext,
+		"https://www.googleapis.com/auth/devstorage.full_control")
+	if err != nil {
+		log.Fatal(err)
+	}
+	client.Get("...")
+}
 
 
 func Example_webServer() {
 func Example_webServer() {
 	// Your credentials should be obtained from the Google
 	// Your credentials should be obtained from the Google
@@ -62,21 +67,34 @@ func ExampleJWTConfigFromJSON() {
 	if err != nil {
 	if err != nil {
 		log.Fatal(err)
 		log.Fatal(err)
 	}
 	}
-	conf, err := google.JWTConfigFromJSON(oauth2.NoContext, data, "https://www.googleapis.com/auth/bigquery")
+	conf, err := google.JWTConfigFromJSON(data, "https://www.googleapis.com/auth/bigquery")
 	if err != nil {
 	if err != nil {
 		log.Fatal(err)
 		log.Fatal(err)
 	}
 	}
 	// Initiate an http.Client. The following GET request will be
 	// Initiate an http.Client. The following GET request will be
 	// authorized and authenticated on the behalf of
 	// authorized and authenticated on the behalf of
 	// your service account.
 	// your service account.
-	client := conf.Client(oauth2.NoContext, nil)
+	client := conf.Client(oauth2.NoContext)
+	client.Get("...")
+}
+
+func ExampleSDKConfig() {
+	// The credentials will be obtained from the first account that
+	// has been authorized with `gcloud auth login`.
+	conf, err := google.NewSDKConfig("")
+	if err != nil {
+		log.Fatal(err)
+	}
+	// Initiate an http.Client. The following GET request will be
+	// authorized and authenticated on the behalf of the SDK user.
+	client := conf.Client(oauth2.NoContext)
 	client.Get("...")
 	client.Get("...")
 }
 }
 
 
 func Example_serviceAccount() {
 func Example_serviceAccount() {
 	// Your credentials should be obtained from the Google
 	// Your credentials should be obtained from the Google
 	// Developer Console (https://console.developers.google.com).
 	// Developer Console (https://console.developers.google.com).
-	conf := &oauth2.JWTConfig{
+	conf := &jwt.Config{
 		Email: "xxx@developer.gserviceaccount.com",
 		Email: "xxx@developer.gserviceaccount.com",
 		// The contents of your RSA private key or your PEM file
 		// The contents of your RSA private key or your PEM file
 		// that contains a private key.
 		// that contains a private key.
@@ -101,7 +119,7 @@ func Example_serviceAccount() {
 	}
 	}
 	// Initiate an http.Client, the following GET request will be
 	// Initiate an http.Client, the following GET request will be
 	// authorized and authenticated on the behalf of user@example.com.
 	// authorized and authenticated on the behalf of user@example.com.
-	client := conf.Client(oauth2.NoContext, nil)
+	client := conf.Client(oauth2.NoContext)
 	client.Get("...")
 	client.Get("...")
 }
 }
 
 

+ 77 - 47
Godeps/_workspace/src/golang.org/x/oauth2/google/google.go

@@ -2,26 +2,28 @@
 // Use of this source code is governed by a BSD-style
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 // license that can be found in the LICENSE file.
 
 
-// Package google provides support for making
-// OAuth2 authorized and authenticated HTTP requests
-// to Google APIs. It supports Web server, client-side,
-// service accounts, Google Compute Engine service accounts,
-// and Google App Engine service accounts authorization
-// and authentications flows:
+// Package google provides support for making OAuth2 authorized and
+// authenticated HTTP requests to Google APIs.
+// It supports the Web server flow, client-side credentials, service accounts,
+// Google Compute Engine service accounts, and Google App Engine service
+// accounts.
 //
 //
 // For more information, please read
 // For more information, please read
-// https://developers.google.com/accounts/docs/OAuth2.
-package google
+// https://developers.google.com/accounts/docs/OAuth2
+// and
+// https://developers.google.com/accounts/application-default-credentials.
+package google // import "golang.org/x/oauth2/google"
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
-
+	"errors"
 	"fmt"
 	"fmt"
-	"net"
-	"net/http"
+	"strings"
 	"time"
 	"time"
 
 
 	"golang.org/x/oauth2"
 	"golang.org/x/oauth2"
+	"golang.org/x/oauth2/jwt"
+	"google.golang.org/cloud/compute/metadata"
 )
 )
 
 
 // Endpoint is Google's OAuth 2.0 endpoint.
 // Endpoint is Google's OAuth 2.0 endpoint.
@@ -33,11 +35,55 @@ var Endpoint = oauth2.Endpoint{
 // JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow.
 // JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow.
 const JWTTokenURL = "https://accounts.google.com/o/oauth2/token"
 const JWTTokenURL = "https://accounts.google.com/o/oauth2/token"
 
 
+// ConfigFromJSON uses a Google Developers Console client_credentials.json
+// file to construct a config.
+// client_credentials.json can be downloadable from https://console.developers.google.com,
+// under "APIs & Auth" > "Credentials". Download the Web application credentials in the
+// JSON format and provide the contents of the file as jsonKey.
+func ConfigFromJSON(jsonKey []byte, scope ...string) (*oauth2.Config, error) {
+	type cred struct {
+		ClientID     string   `json:"client_id"`
+		ClientSecret string   `json:"client_secret"`
+		RedirectURIs []string `json:"redirect_uris"`
+		AuthURI      string   `json:"auth_uri"`
+		TokenURI     string   `json:"token_uri"`
+	}
+	var j struct {
+		Web       *cred `json:"web"`
+		Installed *cred `json:"installed"`
+	}
+	if err := json.Unmarshal(jsonKey, &j); err != nil {
+		return nil, err
+	}
+	var c *cred
+	switch {
+	case j.Web != nil:
+		c = j.Web
+	case j.Installed != nil:
+		c = j.Installed
+	default:
+		return nil, fmt.Errorf("oauth2/google: no credentials found")
+	}
+	if len(c.RedirectURIs) < 1 {
+		return nil, errors.New("oauth2/google: missing redirect URL in the client_credentials.json")
+	}
+	return &oauth2.Config{
+		ClientID:     c.ClientID,
+		ClientSecret: c.ClientSecret,
+		RedirectURL:  c.RedirectURIs[0],
+		Scopes:       scope,
+		Endpoint: oauth2.Endpoint{
+			AuthURL:  c.AuthURI,
+			TokenURL: c.TokenURI,
+		},
+	}, nil
+}
+
 // JWTConfigFromJSON uses a Google Developers service account JSON key file to read
 // JWTConfigFromJSON uses a Google Developers service account JSON key file to read
 // the credentials that authorize and authenticate the requests.
 // the credentials that authorize and authenticate the requests.
 // Create a service account on "Credentials" page under "APIs & Auth" for your
 // Create a service account on "Credentials" page under "APIs & Auth" for your
 // project at https://console.developers.google.com to download a JSON key file.
 // project at https://console.developers.google.com to download a JSON key file.
-func JWTConfigFromJSON(ctx oauth2.Context, jsonKey []byte, scope ...string) (*oauth2.JWTConfig, error) {
+func JWTConfigFromJSON(jsonKey []byte, scope ...string) (*jwt.Config, error) {
 	var key struct {
 	var key struct {
 		Email      string `json:"client_email"`
 		Email      string `json:"client_email"`
 		PrivateKey string `json:"private_key"`
 		PrivateKey string `json:"private_key"`
@@ -45,7 +91,7 @@ func JWTConfigFromJSON(ctx oauth2.Context, jsonKey []byte, scope ...string) (*oa
 	if err := json.Unmarshal(jsonKey, &key); err != nil {
 	if err := json.Unmarshal(jsonKey, &key); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	return &oauth2.JWTConfig{
+	return &jwt.Config{
 		Email:      key.Email,
 		Email:      key.Email,
 		PrivateKey: []byte(key.PrivateKey),
 		PrivateKey: []byte(key.PrivateKey),
 		Scopes:     scope,
 		Scopes:     scope,
@@ -53,12 +99,6 @@ func JWTConfigFromJSON(ctx oauth2.Context, jsonKey []byte, scope ...string) (*oa
 	}, nil
 	}, nil
 }
 }
 
 
-type metaTokenRespBody struct {
-	AccessToken string        `json:"access_token"`
-	ExpiresIn   time.Duration `json:"expires_in"`
-	TokenType   string        `json:"token_type"`
-}
-
 // ComputeTokenSource returns a token source that fetches access tokens
 // ComputeTokenSource returns a token source that fetches access tokens
 // from Google Compute Engine (GCE)'s metadata server. It's only valid to use
 // from Google Compute Engine (GCE)'s metadata server. It's only valid to use
 // this token source if your program is running on a GCE instance.
 // this token source if your program is running on a GCE instance.
@@ -66,50 +106,40 @@ type metaTokenRespBody struct {
 // Further information about retrieving access tokens from the GCE metadata
 // Further information about retrieving access tokens from the GCE metadata
 // server can be found at https://cloud.google.com/compute/docs/authentication.
 // server can be found at https://cloud.google.com/compute/docs/authentication.
 func ComputeTokenSource(account string) oauth2.TokenSource {
 func ComputeTokenSource(account string) oauth2.TokenSource {
-	return &computeSource{account: account}
+	return oauth2.ReuseTokenSource(nil, computeSource{account: account})
 }
 }
 
 
 type computeSource struct {
 type computeSource struct {
 	account string
 	account string
 }
 }
 
 
-var metaClient = &http.Client{
-	Transport: &http.Transport{
-		Dial: (&net.Dialer{
-			Timeout:   750 * time.Millisecond,
-			KeepAlive: 30 * time.Second,
-		}).Dial,
-		ResponseHeaderTimeout: 750 * time.Millisecond,
-	},
-}
-
-func (cs *computeSource) Token() (*oauth2.Token, error) {
+func (cs computeSource) Token() (*oauth2.Token, error) {
+	if !metadata.OnGCE() {
+		return nil, errors.New("oauth2/google: can't get a token from the metadata service; not running on GCE")
+	}
 	acct := cs.account
 	acct := cs.account
 	if acct == "" {
 	if acct == "" {
 		acct = "default"
 		acct = "default"
 	}
 	}
-	u := "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/" + acct + "/token"
-	req, err := http.NewRequest("GET", u, nil)
+	tokenJSON, err := metadata.Get("instance/service-accounts/" + acct + "/token")
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	req.Header.Add("X-Google-Metadata-Request", "True")
-	resp, err := metaClient.Do(req)
-	if err != nil {
-		return nil, err
-	}
-	defer resp.Body.Close()
-	if resp.StatusCode < 200 || resp.StatusCode > 299 {
-		return nil, fmt.Errorf("oauth2: can't retrieve a token from metadata server, status code: %d", resp.StatusCode)
+	var res struct {
+		AccessToken  string `json:"access_token"`
+		ExpiresInSec int    `json:"expires_in"`
+		TokenType    string `json:"token_type"`
 	}
 	}
-	var tokenResp metaTokenRespBody
-	err = json.NewDecoder(resp.Body).Decode(&tokenResp)
+	err = json.NewDecoder(strings.NewReader(tokenJSON)).Decode(&res)
 	if err != nil {
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("oauth2/google: invalid token JSON from metadata: %v", err)
+	}
+	if res.ExpiresInSec == 0 || res.AccessToken == "" {
+		return nil, fmt.Errorf("oauth2/google: incomplete token received from metadata")
 	}
 	}
 	return &oauth2.Token{
 	return &oauth2.Token{
-		AccessToken: tokenResp.AccessToken,
-		TokenType:   tokenResp.TokenType,
-		Expiry:      time.Now().Add(tokenResp.ExpiresIn * time.Second),
+		AccessToken: res.AccessToken,
+		TokenType:   res.TokenType,
+		Expiry:      time.Now().Add(time.Duration(res.ExpiresInSec) * time.Second),
 	}, nil
 	}, nil
 }
 }

+ 67 - 0
Godeps/_workspace/src/golang.org/x/oauth2/google/google_test.go

@@ -0,0 +1,67 @@
+// Copyright 2015 The oauth2 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 (
+	"strings"
+	"testing"
+)
+
+var webJSONKey = []byte(`
+{
+    "web": {
+        "auth_uri": "https://google.com/o/oauth2/auth",
+        "client_secret": "3Oknc4jS_wA2r9i",
+        "token_uri": "https://google.com/o/oauth2/token",
+        "client_email": "222-nprqovg5k43uum874cs9osjt2koe97g8@developer.gserviceaccount.com",
+        "redirect_uris": ["https://www.example.com/oauth2callback"],
+        "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/222-nprqovg5k43uum874cs9osjt2koe97g8@developer.gserviceaccount.com",
+        "client_id": "222-nprqovg5k43uum874cs9osjt2koe97g8.apps.googleusercontent.com",
+        "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
+        "javascript_origins": ["https://www.example.com"]
+    }
+}`)
+
+var installedJSONKey = []byte(`{
+  "installed": {
+      "client_id": "222-installed.apps.googleusercontent.com",
+      "redirect_uris": ["https://www.example.com/oauth2callback"]
+    }
+}`)
+
+func TestConfigFromJSON(t *testing.T) {
+	conf, err := ConfigFromJSON(webJSONKey, "scope1", "scope2")
+	if err != nil {
+		t.Error(err)
+	}
+	if got, want := conf.ClientID, "222-nprqovg5k43uum874cs9osjt2koe97g8.apps.googleusercontent.com"; got != want {
+		t.Errorf("ClientID = %q; want %q", got, want)
+	}
+	if got, want := conf.ClientSecret, "3Oknc4jS_wA2r9i"; got != want {
+		t.Errorf("ClientSecret = %q; want %q", got, want)
+	}
+	if got, want := conf.RedirectURL, "https://www.example.com/oauth2callback"; got != want {
+		t.Errorf("RedictURL = %q; want %q", got, want)
+	}
+	if got, want := strings.Join(conf.Scopes, ","), "scope1,scope2"; got != want {
+		t.Errorf("Scopes = %q; want %q", got, want)
+	}
+	if got, want := conf.Endpoint.AuthURL, "https://google.com/o/oauth2/auth"; got != want {
+		t.Errorf("AuthURL = %q; want %q", got, want)
+	}
+	if got, want := conf.Endpoint.TokenURL, "https://google.com/o/oauth2/token"; got != want {
+		t.Errorf("TokenURL = %q; want %q", got, want)
+	}
+}
+
+func TestConfigFromJSON_Installed(t *testing.T) {
+	conf, err := ConfigFromJSON(installedJSONKey)
+	if err != nil {
+		t.Error(err)
+	}
+	if got, want := conf.ClientID, "222-installed.apps.googleusercontent.com"; got != want {
+		t.Errorf("ClientID = %q; want %q", got, want)
+	}
+}

+ 168 - 0
Godeps/_workspace/src/golang.org/x/oauth2/google/sdk.go

@@ -0,0 +1,168 @@
+// Copyright 2015 The oauth2 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 (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+	"os"
+	"os/user"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"time"
+
+	"golang.org/x/net/context"
+	"golang.org/x/oauth2"
+	"golang.org/x/oauth2/internal"
+)
+
+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.
+type SDKConfig struct {
+	conf         oauth2.Config
+	initialToken *oauth2.Token
+}
+
+// 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 NewSDKConfig(account string) (*SDKConfig, error) {
+	configPath, err := sdkConfigPath()
+	if err != nil {
+		return nil, fmt.Errorf("oauth2/google: error getting SDK config path: %v", err)
+	}
+	credentialsPath := filepath.Join(configPath, "credentials")
+	f, err := os.Open(credentialsPath)
+	if err != nil {
+		return nil, fmt.Errorf("oauth2/google: failed to load SDK credentials: %v", err)
+	}
+	defer f.Close()
+
+	var c sdkCredentials
+	if err := json.NewDecoder(f).Decode(&c); err != nil {
+		return nil, fmt.Errorf("oauth2/google: failed to decode SDK credentials from %q: %v", credentialsPath, err)
+	}
+	if len(c.Data) == 0 {
+		return nil, fmt.Errorf("oauth2/google: no credentials found in %q, run `gcloud auth login` to create one", credentialsPath)
+	}
+	if account == "" {
+		propertiesPath := filepath.Join(configPath, "properties")
+		f, err := os.Open(propertiesPath)
+		if err != nil {
+			return nil, fmt.Errorf("oauth2/google: failed to load SDK properties: %v", err)
+		}
+		defer f.Close()
+		ini, err := internal.ParseINI(f)
+		if err != nil {
+			return nil, fmt.Errorf("oauth2/google: failed to parse SDK properties %q: %v", propertiesPath, err)
+		}
+		core, ok := ini["core"]
+		if !ok {
+			return nil, fmt.Errorf("oauth2/google: failed to find [core] section in %v", ini)
+		}
+		active, ok := core["account"]
+		if !ok {
+			return nil, fmt.Errorf("oauth2/google: failed to find %q attribute in %v", "account", core)
+		}
+		account = active
+	}
+
+	for _, d := range c.Data {
+		if account == "" || d.Key.Account == account {
+			if d.Credential.AccessToken == "" && d.Credential.RefreshToken == "" {
+				return nil, fmt.Errorf("oauth2/google: no token available for account %q", account)
+			}
+			var expiry time.Time
+			if d.Credential.TokenExpiry != nil {
+				expiry = *d.Credential.TokenExpiry
+			}
+			return &SDKConfig{
+				conf: oauth2.Config{
+					ClientID:     d.Credential.ClientID,
+					ClientSecret: d.Credential.ClientSecret,
+					Scopes:       strings.Split(d.Key.Scope, " "),
+					Endpoint:     Endpoint,
+					RedirectURL:  "oob",
+				},
+				initialToken: &oauth2.Token{
+					AccessToken:  d.Credential.AccessToken,
+					RefreshToken: d.Credential.RefreshToken,
+					Expiry:       expiry,
+				},
+			}, nil
+		}
+	}
+	return nil, fmt.Errorf("oauth2/google: no such credentials for account %q", account)
+}
+
+// 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.
+func (c *SDKConfig) Client(ctx context.Context) *http.Client {
+	return &http.Client{
+		Transport: &oauth2.Transport{
+			Source: c.TokenSource(ctx),
+		},
+	}
+}
+
+// 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.
+func (c *SDKConfig) TokenSource(ctx context.Context) oauth2.TokenSource {
+	return c.conf.TokenSource(ctx, c.initialToken)
+}
+
+// Scopes are the OAuth 2.0 scopes the current account is authorized for.
+func (c *SDKConfig) Scopes() []string {
+	return c.conf.Scopes
+}
+
+// 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
+	}
+	homeDir := guessUnixHomeDir()
+	if homeDir == "" {
+		return "", errors.New("unable to get current user home directory: os/user lookup failed; $HOME is empty")
+	}
+	return filepath.Join(homeDir, ".config", "gcloud"), nil
+}
+
+func guessUnixHomeDir() string {
+	usr, err := user.Current()
+	if err == nil {
+		return usr.HomeDir
+	}
+	return os.Getenv("HOME")
+}

+ 46 - 0
Godeps/_workspace/src/golang.org/x/oauth2/google/sdk_test.go

@@ -0,0 +1,46 @@
+// Copyright 2015 The oauth2 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 "testing"
+
+func TestSDKConfig(t *testing.T) {
+	sdkConfigPath = func() (string, error) {
+		return "testdata/gcloud", nil
+	}
+
+	tests := []struct {
+		account     string
+		accessToken string
+		err         bool
+	}{
+		{"", "bar_access_token", false},
+		{"foo@example.com", "foo_access_token", false},
+		{"bar@example.com", "bar_access_token", false},
+		{"baz@serviceaccount.example.com", "", true},
+	}
+	for _, tt := range tests {
+		c, err := NewSDKConfig(tt.account)
+		if got, want := err != nil, tt.err; got != want {
+			if !tt.err {
+				t.Errorf("expected no error, got error: %v", tt.err, err)
+			} else {
+				t.Errorf("expected error, got none")
+			}
+			continue
+		}
+		if err != nil {
+			continue
+		}
+		tok := c.initialToken
+		if tok == nil {
+			t.Errorf("expected token %q, got: nil", tt.accessToken)
+			continue
+		}
+		if tok.AccessToken != tt.accessToken {
+			t.Errorf("expected token %q, got: %q", tt.accessToken, tok.AccessToken)
+		}
+	}
+}

+ 0 - 68
Godeps/_workspace/src/golang.org/x/oauth2/google/source_appengine.go

@@ -1,68 +0,0 @@
-// Copyright 2014 The oauth2 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 (
-	"errors"
-	"sort"
-	"strings"
-	"sync"
-	"time"
-
-	"golang.org/x/oauth2"
-)
-
-var (
-	aeTokensMu sync.Mutex // guards aeTokens and appEngineTokenSource.key
-
-	// aeTokens helps the fetched tokens to be reused until their expiration.
-	aeTokens = make(map[string]*tokenLock) // key is '\0'-separated scopes
-)
-
-var errInvalidContext = errors.New("oauth2: a valid appengine.Context is required")
-
-type tokenLock struct {
-	mu sync.Mutex // guards t; held while updating t
-	t  *oauth2.Token
-}
-
-type appEngineTokenSource struct {
-	ctx    oauth2.Context
-	scopes []string
-	key    string // guarded by package-level mutex, aeTokensMu
-
-	// fetcherFunc makes the actual RPC to fetch a new access token with an expiry time.
-	// Provider of this function is responsible to assert that the given context is valid.
-	fetcherFunc func(ctx oauth2.Context, scope ...string) (string, time.Time, error)
-}
-
-func (ts *appEngineTokenSource) Token() (*oauth2.Token, error) {
-	aeTokensMu.Lock()
-	if ts.key == "" {
-		sort.Sort(sort.StringSlice(ts.scopes))
-		ts.key = strings.Join(ts.scopes, string(0))
-	}
-	tok, ok := aeTokens[ts.key]
-	if !ok {
-		tok = &tokenLock{}
-		aeTokens[ts.key] = tok
-	}
-	aeTokensMu.Unlock()
-
-	tok.mu.Lock()
-	defer tok.mu.Unlock()
-	if tok.t != nil && !tok.t.Expired() {
-		return tok.t, nil
-	}
-	access, exp, err := ts.fetcherFunc(ts.ctx, ts.scopes...)
-	if err != nil {
-		return nil, err
-	}
-	tok.t = &oauth2.Token{
-		AccessToken: access,
-		Expiry:      exp,
-	}
-	return tok.t, nil
-}

+ 122 - 0
Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/credentials

@@ -0,0 +1,122 @@
+{
+  "data": [
+    {
+      "credential": {
+        "_class": "OAuth2Credentials",
+        "_module": "oauth2client.client",
+        "access_token": "foo_access_token",
+        "client_id": "foo_client_id",
+        "client_secret": "foo_client_secret",
+        "id_token": {
+          "at_hash": "foo_at_hash",
+          "aud": "foo_aud",
+          "azp": "foo_azp",
+          "cid": "foo_cid",
+          "email": "foo@example.com",
+          "email_verified": true,
+          "exp": 1420573614,
+          "iat": 1420569714,
+          "id": "1337",
+          "iss": "accounts.google.com",
+          "sub": "1337",
+          "token_hash": "foo_token_hash",
+          "verified_email": true
+        },
+        "invalid": false,
+        "refresh_token": "foo_refresh_token",
+        "revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
+        "token_expiry": "2015-01-09T00:51:51Z",
+        "token_response": {
+          "access_token": "foo_access_token",
+          "expires_in": 3600,
+          "id_token": "foo_id_token",
+          "token_type": "Bearer"
+        },
+        "token_uri": "https://accounts.google.com/o/oauth2/token",
+        "user_agent": "Cloud SDK Command Line Tool"
+      },
+      "key": {
+        "account": "foo@example.com",
+        "clientId": "foo_client_id",
+        "scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting",
+        "type": "google-cloud-sdk"
+      }
+    },
+    {
+      "credential": {
+        "_class": "OAuth2Credentials",
+        "_module": "oauth2client.client",
+        "access_token": "bar_access_token",
+        "client_id": "bar_client_id",
+        "client_secret": "bar_client_secret",
+        "id_token": {
+          "at_hash": "bar_at_hash",
+          "aud": "bar_aud",
+          "azp": "bar_azp",
+          "cid": "bar_cid",
+          "email": "bar@example.com",
+          "email_verified": true,
+          "exp": 1420573614,
+          "iat": 1420569714,
+          "id": "1337",
+          "iss": "accounts.google.com",
+          "sub": "1337",
+          "token_hash": "bar_token_hash",
+          "verified_email": true
+        },
+        "invalid": false,
+        "refresh_token": "bar_refresh_token",
+        "revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
+        "token_expiry": "2015-01-09T00:51:51Z",
+        "token_response": {
+          "access_token": "bar_access_token",
+          "expires_in": 3600,
+          "id_token": "bar_id_token",
+          "token_type": "Bearer"
+        },
+        "token_uri": "https://accounts.google.com/o/oauth2/token",
+        "user_agent": "Cloud SDK Command Line Tool"
+      },
+      "key": {
+        "account": "bar@example.com",
+        "clientId": "bar_client_id",
+        "scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting",
+        "type": "google-cloud-sdk"
+      }
+    },
+    {
+      "credential": {
+        "_class": "ServiceAccountCredentials",
+        "_kwargs": {},
+        "_module": "oauth2client.client",
+        "_private_key_id": "00000000000000000000000000000000",
+        "_private_key_pkcs8_text": "-----BEGIN RSA PRIVATE KEY-----\nMIICWwIBAAKBgQCt3fpiynPSaUhWSIKMGV331zudwJ6GkGmvQtwsoK2S2LbvnSwU\nNxgj4fp08kIDR5p26wF4+t/HrKydMwzftXBfZ9UmLVJgRdSswmS5SmChCrfDS5OE\nvFFcN5+6w1w8/Nu657PF/dse8T0bV95YrqyoR0Osy8WHrUOMSIIbC3hRuwIDAQAB\nAoGAJrGE/KFjn0sQ7yrZ6sXmdLawrM3mObo/2uI9T60+k7SpGbBX0/Pi6nFrJMWZ\nTVONG7P3Mu5aCPzzuVRYJB0j8aldSfzABTY3HKoWCczqw1OztJiEseXGiYz4QOyr\nYU3qDyEpdhS6q6wcoLKGH+hqRmz6pcSEsc8XzOOu7s4xW8kCQQDkc75HjhbarCnd\nJJGMe3U76+6UGmdK67ltZj6k6xoB5WbTNChY9TAyI2JC+ppYV89zv3ssj4L+02u3\nHIHFGxsHAkEAwtU1qYb1tScpchPobnYUFiVKJ7KA8EZaHVaJJODW/cghTCV7BxcJ\nbgVvlmk4lFKn3lPKAgWw7PdQsBTVBUcCrQJATPwoIirizrv3u5soJUQxZIkENAqV\nxmybZx9uetrzP7JTrVbFRf0SScMcyN90hdLJiQL8+i4+gaszgFht7sNMnwJAAbfj\nq0UXcauQwALQ7/h2oONfTg5S+MuGC/AxcXPSMZbMRGGoPh3D5YaCv27aIuS/ukQ+\n6dmm/9AGlCb64fsIWQJAPaokbjIifo+LwC5gyK73Mc4t8nAOSZDenzd/2f6TCq76\nS1dcnKiPxaED7W/y6LJiuBT2rbZiQ2L93NJpFZD/UA==\n-----END RSA PRIVATE KEY-----\n",
+        "_revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
+        "_scopes": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting",
+        "_service_account_email": "baz@serviceaccount.example.com",
+        "_service_account_id": "baz.serviceaccount.example.com",
+        "_token_uri": "https://accounts.google.com/o/oauth2/token",
+        "_user_agent": "Cloud SDK Command Line Tool",
+        "access_token": null,
+        "assertion_type": null,
+        "client_id": null,
+        "client_secret": null,
+        "id_token": null,
+        "invalid": false,
+        "refresh_token": null,
+        "revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
+        "service_account_name": "baz@serviceaccount.example.com",
+        "token_expiry": null,
+        "token_response": null,
+        "user_agent": "Cloud SDK Command Line Tool"
+      },
+      "key": {
+        "account": "baz@serviceaccount.example.com",
+        "clientId": "baz_client_id",
+        "scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting",
+        "type": "google-cloud-sdk"
+      }
+    }
+  ],
+  "file_version": 1
+}

+ 2 - 0
Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/properties

@@ -0,0 +1,2 @@
+[core]
+account = bar@example.com

+ 34 - 2
Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2.go

@@ -6,10 +6,14 @@
 package internal
 package internal
 
 
 import (
 import (
+	"bufio"
 	"crypto/rsa"
 	"crypto/rsa"
 	"crypto/x509"
 	"crypto/x509"
 	"encoding/pem"
 	"encoding/pem"
 	"errors"
 	"errors"
+	"fmt"
+	"io"
+	"strings"
 )
 )
 
 
 // ParseKey converts the binary contents of a private key file
 // ParseKey converts the binary contents of a private key file
@@ -26,12 +30,40 @@ func ParseKey(key []byte) (*rsa.PrivateKey, error) {
 	if err != nil {
 	if err != nil {
 		parsedKey, err = x509.ParsePKCS1PrivateKey(key)
 		parsedKey, err = x509.ParsePKCS1PrivateKey(key)
 		if err != nil {
 		if err != nil {
-			return nil, err
+			return nil, fmt.Errorf("private key should be a PEM or plain PKSC1 or PKCS8; parse error: %v", err)
 		}
 		}
 	}
 	}
 	parsed, ok := parsedKey.(*rsa.PrivateKey)
 	parsed, ok := parsedKey.(*rsa.PrivateKey)
 	if !ok {
 	if !ok {
-		return nil, errors.New("oauth2: private key is invalid")
+		return nil, errors.New("private key is invalid")
 	}
 	}
 	return parsed, nil
 	return parsed, nil
 }
 }
+
+func ParseINI(ini io.Reader) (map[string]map[string]string, error) {
+	result := map[string]map[string]string{
+		"": map[string]string{}, // root section
+	}
+	scanner := bufio.NewScanner(ini)
+	currentSection := ""
+	for scanner.Scan() {
+		line := strings.TrimSpace(scanner.Text())
+		if strings.HasPrefix(line, ";") {
+			// comment.
+			continue
+		}
+		if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
+			currentSection = strings.TrimSpace(line[1 : len(line)-1])
+			result[currentSection] = map[string]string{}
+			continue
+		}
+		parts := strings.SplitN(line, "=", 2)
+		if len(parts) == 2 && parts[0] != "" {
+			result[currentSection][strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
+		}
+	}
+	if err := scanner.Err(); err != nil {
+		return nil, fmt.Errorf("error scanning ini: %v", err)
+	}
+	return result, nil
+}

+ 62 - 0
Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2_test.go

@@ -0,0 +1,62 @@
+// Copyright 2014 The oauth2 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 internal contains support packages for oauth2 package.
+package internal
+
+import (
+	"reflect"
+	"strings"
+	"testing"
+)
+
+func TestParseINI(t *testing.T) {
+	tests := []struct {
+		ini  string
+		want map[string]map[string]string
+	}{
+		{
+			`root = toor
+[foo]  
+bar = hop
+ini = nin
+`,
+			map[string]map[string]string{
+				"":    map[string]string{"root": "toor"},
+				"foo": map[string]string{"bar": "hop", "ini": "nin"},
+			},
+		},
+		{
+			`[empty]
+[section]
+empty=
+`,
+			map[string]map[string]string{
+				"":        map[string]string{},
+				"empty":   map[string]string{},
+				"section": map[string]string{"empty": ""},
+			},
+		},
+		{
+			`ignore
+[invalid
+=stuff
+;comment=true
+`,
+			map[string]map[string]string{
+				"": map[string]string{},
+			},
+		},
+	}
+	for _, tt := range tests {
+		result, err := ParseINI(strings.NewReader(tt.ini))
+		if err != nil {
+			t.Errorf("ParseINI(%q) error %v, want: no error", tt.ini, err)
+			continue
+		}
+		if !reflect.DeepEqual(result, tt.want) {
+			t.Errorf("ParseINI(%q) = %#v, want: %#v", tt.ini, result, tt.want)
+		}
+	}
+}

+ 1 - 1
Godeps/_workspace/src/golang.org/x/oauth2/jws/jws.go

@@ -4,7 +4,7 @@
 
 
 // Package jws provides encoding and decoding utilities for
 // Package jws provides encoding and decoding utilities for
 // signed JWS messages.
 // signed JWS messages.
-package jws
+package jws // import "golang.org/x/oauth2/jws"
 
 
 import (
 import (
 	"bytes"
 	"bytes"

+ 31 - 0
Godeps/_workspace/src/golang.org/x/oauth2/jwt/example_test.go

@@ -0,0 +1,31 @@
+// Copyright 2014 The oauth2 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 jwt_test
+
+import (
+	"golang.org/x/oauth2"
+	"golang.org/x/oauth2/jwt"
+)
+
+func ExampleJWTConfig() {
+	conf := &jwt.Config{
+		Email: "xxx@developer.com",
+		// The contents of your RSA private key or your PEM file
+		// that contains a private key.
+		// If you have a p12 file instead, you
+		// can use `openssl` to export the private key into a pem file.
+		//
+		//    $ openssl pkcs12 -in key.p12 -out key.pem -nodes
+		//
+		// It only supports PEM containers with no passphrase.
+		PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."),
+		Subject:    "user@example.com",
+		TokenURL:   "https://provider.com/o/oauth2/token",
+	}
+	// Initiate an http.Client, the following GET request will be
+	// authorized and authenticated on the behalf of user@example.com.
+	client := conf.Client(oauth2.NoContext)
+	client.Get("...")
+}

+ 23 - 31
Godeps/_workspace/src/golang.org/x/oauth2/jwt.go → Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt.go

@@ -2,7 +2,11 @@
 // Use of this source code is governed by a BSD-style
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 // license that can be found in the LICENSE file.
 
 
-package oauth2
+// Package jwt implements the OAuth 2.0 JSON Web Token flow, commonly
+// known as "two-legged OAuth 2.0".
+//
+// See: https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12
+package jwt
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
@@ -14,6 +18,8 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
+	"golang.org/x/net/context"
+	"golang.org/x/oauth2"
 	"golang.org/x/oauth2/internal"
 	"golang.org/x/oauth2/internal"
 	"golang.org/x/oauth2/jws"
 	"golang.org/x/oauth2/jws"
 )
 )
@@ -23,9 +29,9 @@ var (
 	defaultHeader    = &jws.Header{Algorithm: "RS256", Typ: "JWT"}
 	defaultHeader    = &jws.Header{Algorithm: "RS256", Typ: "JWT"}
 )
 )
 
 
-// JWTConfig is the configuration for using JWT to fetch tokens,
-// commonly known as "two-legged OAuth".
-type JWTConfig struct {
+// Config is the configuration for using JWT to fetch tokens,
+// commonly known as "two-legged OAuth 2.0".
+type Config struct {
 	// Email is the OAuth client identifier used when communicating with
 	// Email is the OAuth client identifier used when communicating with
 	// the configured OAuth provider.
 	// the configured OAuth provider.
 	Email string
 	Email string
@@ -52,47 +58,32 @@ type JWTConfig struct {
 
 
 // TokenSource returns a JWT TokenSource using the configuration
 // TokenSource returns a JWT TokenSource using the configuration
 // in c and the HTTP client from the provided context.
 // in c and the HTTP client from the provided context.
-//
-// The returned TokenSource only does JWT requests when necessary but
-// otherwise returns the same token repeatedly until it expires.
-//
-// The provided initialToken may be nil, in which case the first
-// call to TokenSource will do a new JWT request.
-func (c *JWTConfig) TokenSource(ctx Context, initialToken *Token) TokenSource {
-	return &newWhenNeededSource{
-		t:   initialToken,
-		new: jwtSource{ctx, c},
-	}
+func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource {
+	return oauth2.ReuseTokenSource(nil, jwtSource{ctx, c})
 }
 }
 
 
 // Client returns an HTTP client wrapping the context's
 // Client returns an HTTP client wrapping the context's
 // HTTP transport and adding Authorization headers with tokens
 // HTTP transport and adding Authorization headers with tokens
 // obtained from c.
 // obtained from c.
 //
 //
-// The provided initialToken may be nil, in which case the first
-// call to TokenSource will do a new JWT request.
-//
 // The returned client and its Transport should not be modified.
 // The returned client and its Transport should not be modified.
-func (c *JWTConfig) Client(ctx Context, initialToken *Token) *http.Client {
-	return NewClient(ctx, c.TokenSource(ctx, initialToken))
+func (c *Config) Client(ctx context.Context) *http.Client {
+	return oauth2.NewClient(ctx, c.TokenSource(ctx))
 }
 }
 
 
 // jwtSource is a source that always does a signed JWT request for a token.
 // jwtSource is a source that always does a signed JWT request for a token.
-// It should typically be wrapped with a newWhenNeededSource.
+// It should typically be wrapped with a reuseTokenSource.
 type jwtSource struct {
 type jwtSource struct {
-	ctx  Context
-	conf *JWTConfig
+	ctx  context.Context
+	conf *Config
 }
 }
 
 
-func (js jwtSource) Token() (*Token, error) {
+func (js jwtSource) Token() (*oauth2.Token, error) {
 	pk, err := internal.ParseKey(js.conf.PrivateKey)
 	pk, err := internal.ParseKey(js.conf.PrivateKey)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	hc, err := contextClient(js.ctx)
-	if err != nil {
-		return nil, err
-	}
+	hc := oauth2.NewClient(js.ctx, nil)
 	claimSet := &jws.ClaimSet{
 	claimSet := &jws.ClaimSet{
 		Iss:   js.conf.Email,
 		Iss:   js.conf.Email,
 		Scope: strings.Join(js.conf.Scopes, " "),
 		Scope: strings.Join(js.conf.Scopes, " "),
@@ -133,12 +124,13 @@ func (js jwtSource) Token() (*Token, error) {
 	if err := json.Unmarshal(body, &tokenRes); err != nil {
 	if err := json.Unmarshal(body, &tokenRes); err != nil {
 		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
 		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
 	}
 	}
-	token := &Token{
+	token := &oauth2.Token{
 		AccessToken: tokenRes.AccessToken,
 		AccessToken: tokenRes.AccessToken,
 		TokenType:   tokenRes.TokenType,
 		TokenType:   tokenRes.TokenType,
-		raw:         make(map[string]interface{}),
 	}
 	}
-	json.Unmarshal(body, &token.raw) // no error checks for optional fields
+	raw := make(map[string]interface{})
+	json.Unmarshal(body, &raw) // no error checks for optional fields
+	token = token.WithExtra(raw)
 
 
 	if secs := tokenRes.ExpiresIn; secs > 0 {
 	if secs := tokenRes.ExpiresIn; secs > 0 {
 		token.Expiry = time.Now().Add(time.Duration(secs) * time.Second)
 		token.Expiry = time.Now().Add(time.Duration(secs) * time.Second)

+ 22 - 14
Godeps/_workspace/src/golang.org/x/oauth2/jwt_test.go → Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt_test.go

@@ -2,12 +2,14 @@
 // Use of this source code is governed by a BSD-style
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 // license that can be found in the LICENSE file.
 
 
-package oauth2
+package jwt
 
 
 import (
 import (
 	"net/http"
 	"net/http"
 	"net/http/httptest"
 	"net/http/httptest"
 	"testing"
 	"testing"
+
+	"golang.org/x/oauth2"
 )
 )
 
 
 var dummyPrivateKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
 var dummyPrivateKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
@@ -50,17 +52,17 @@ func TestJWTFetch_JSONResponse(t *testing.T) {
 	}))
 	}))
 	defer ts.Close()
 	defer ts.Close()
 
 
-	conf := &JWTConfig{
+	conf := &Config{
 		Email:      "aaa@xxx.com",
 		Email:      "aaa@xxx.com",
 		PrivateKey: dummyPrivateKey,
 		PrivateKey: dummyPrivateKey,
 		TokenURL:   ts.URL,
 		TokenURL:   ts.URL,
 	}
 	}
-	tok, err := conf.TokenSource(NoContext, nil).Token()
+	tok, err := conf.TokenSource(oauth2.NoContext).Token()
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if tok.Expired() {
-		t.Errorf("Token shouldn't be expired")
+	if !tok.Valid() {
+		t.Errorf("Token invalid")
 	}
 	}
 	if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
 	if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
 		t.Errorf("Unexpected access token, %#v", tok.AccessToken)
 		t.Errorf("Unexpected access token, %#v", tok.AccessToken)
@@ -84,24 +86,30 @@ func TestJWTFetch_BadResponse(t *testing.T) {
 	}))
 	}))
 	defer ts.Close()
 	defer ts.Close()
 
 
-	conf := &JWTConfig{
+	conf := &Config{
 		Email:      "aaa@xxx.com",
 		Email:      "aaa@xxx.com",
 		PrivateKey: dummyPrivateKey,
 		PrivateKey: dummyPrivateKey,
 		TokenURL:   ts.URL,
 		TokenURL:   ts.URL,
 	}
 	}
-	tok, err := conf.TokenSource(NoContext, nil).Token()
+	tok, err := conf.TokenSource(oauth2.NoContext).Token()
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
+	if tok == nil {
+		t.Fatalf("token is nil")
+	}
+	if tok.Valid() {
+		t.Errorf("token is valid. want invalid.")
+	}
 	if tok.AccessToken != "" {
 	if tok.AccessToken != "" {
-		t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
+		t.Errorf("Unexpected non-empty access token %q.", tok.AccessToken)
 	}
 	}
-	if tok.TokenType != "bearer" {
-		t.Errorf("Unexpected token type, %#v.", tok.TokenType)
+	if want := "bearer"; tok.TokenType != want {
+		t.Errorf("TokenType = %q; want %q", tok.TokenType, want)
 	}
 	}
 	scope := tok.Extra("scope")
 	scope := tok.Extra("scope")
-	if scope != "user" {
-		t.Errorf("Unexpected value for scope: %v", scope)
+	if want := "user"; scope != want {
+		t.Errorf("token scope = %q; want %q", scope, want)
 	}
 	}
 }
 }
 
 
@@ -111,12 +119,12 @@ func TestJWTFetch_BadResponseType(t *testing.T) {
 		w.Write([]byte(`{"access_token":123, "scope": "user", "token_type": "bearer"}`))
 		w.Write([]byte(`{"access_token":123, "scope": "user", "token_type": "bearer"}`))
 	}))
 	}))
 	defer ts.Close()
 	defer ts.Close()
-	conf := &JWTConfig{
+	conf := &Config{
 		Email:      "aaa@xxx.com",
 		Email:      "aaa@xxx.com",
 		PrivateKey: dummyPrivateKey,
 		PrivateKey: dummyPrivateKey,
 		TokenURL:   ts.URL,
 		TokenURL:   ts.URL,
 	}
 	}
-	tok, err := conf.TokenSource(NoContext, nil).Token()
+	tok, err := conf.TokenSource(oauth2.NoContext).Token()
 	if err == nil {
 	if err == nil {
 		t.Error("got a token; expected error")
 		t.Error("got a token; expected error")
 		if tok.AccessToken != "" {
 		if tok.AccessToken != "" {

+ 16 - 0
Godeps/_workspace/src/golang.org/x/oauth2/linkedin/linkedin.go

@@ -0,0 +1,16 @@
+// Copyright 2015 The oauth2 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 linkedin provides constants for using OAuth2 to access LinkedIn.
+package linkedin // import "golang.org/x/oauth2/linkedin"
+
+import (
+	"golang.org/x/oauth2"
+)
+
+// Endpoint is LinkedIn's OAuth 2.0 endpoint.
+var Endpoint = oauth2.Endpoint{
+	AuthURL:  "https://www.linkedin.com/uas/oauth2/authorization",
+	TokenURL: "https://www.linkedin.com/uas/oauth2/accessToken",
+}

+ 183 - 81
Godeps/_workspace/src/golang.org/x/oauth2/oauth2.go

@@ -5,7 +5,7 @@
 // Package oauth2 provides support for making
 // Package oauth2 provides support for making
 // OAuth2 authorized and authenticated HTTP requests.
 // OAuth2 authorized and authenticated HTTP requests.
 // It can additionally grant authorization with Bearer JWT.
 // It can additionally grant authorization with Bearer JWT.
-package oauth2
+package oauth2 // import "golang.org/x/oauth2"
 
 
 import (
 import (
 	"bytes"
 	"bytes"
@@ -25,18 +25,12 @@ import (
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
-// Context can be an golang.org/x/net.Context, or an App Engine Context.
-// In the future these will be unified.
-// If you don't care and aren't running on App Engine, you may use NoContext.
-type Context interface{}
-
-// NoContext is the default context. If you're not running this code
-// on App Engine or not using golang.org/x/net.Context to provide a custom
-// HTTP client, you should use NoContext.
-var NoContext Context = nil
+// NoContext is the default context you should supply if not using
+// your own context.Context (see https://golang.org/x/net/context).
+var NoContext = context.TODO()
 
 
 // Config describes a typical 3-legged OAuth2 flow, with both the
 // Config describes a typical 3-legged OAuth2 flow, with both the
-// client application information and the server's URLs.
+// client application information and the server's endpoint URLs.
 type Config struct {
 type Config struct {
 	// ClientID is the application's ID.
 	// ClientID is the application's ID.
 	ClientID string
 	ClientID string
@@ -45,9 +39,9 @@ type Config struct {
 	ClientSecret string
 	ClientSecret string
 
 
 	// Endpoint contains the resource server's token endpoint
 	// Endpoint contains the resource server's token endpoint
-	// URLs.  These are supplied by the server and are often
-	// available via site-specific packages (for example,
-	// google.Endpoint or github.Endpoint)
+	// URLs. These are constants specific to each server and are
+	// often available via site-specific packages, such as
+	// google.Endpoint or github.Endpoint.
 	Endpoint Endpoint
 	Endpoint Endpoint
 
 
 	// RedirectURL is the URL to redirect users going through
 	// RedirectURL is the URL to redirect users going through
@@ -61,6 +55,8 @@ type Config struct {
 // A TokenSource is anything that can return a token.
 // A TokenSource is anything that can return a token.
 type TokenSource interface {
 type TokenSource interface {
 	// Token returns a token or an error.
 	// Token returns a token or an error.
+	// Token must be safe for concurrent use by multiple goroutines.
+	// The returned Token must not be modified.
 	Token() (*Token, error)
 	Token() (*Token, error)
 }
 }
 
 
@@ -77,28 +73,34 @@ var (
 	// "access_type" field that gets sent in the URL returned by
 	// "access_type" field that gets sent in the URL returned by
 	// AuthCodeURL.
 	// AuthCodeURL.
 	//
 	//
-	// Online (the default if neither is specified) is the default.
-	// If your application needs to refresh access tokens when the
-	// user is not present at the browser, then use offline. This
-	// will result in your application obtaining a refresh token
-	// the first time your application exchanges an authorization
+	// Online is the default if neither is specified. If your
+	// application needs to refresh access tokens when the user
+	// is not present at the browser, then use offline. This will
+	// result in your application obtaining a refresh token the
+	// first time your application exchanges an authorization
 	// code for a user.
 	// code for a user.
-	AccessTypeOnline  AuthCodeOption = setParam{"access_type", "online"}
-	AccessTypeOffline AuthCodeOption = setParam{"access_type", "offline"}
+	AccessTypeOnline  AuthCodeOption = SetParam("access_type", "online")
+	AccessTypeOffline AuthCodeOption = SetParam("access_type", "offline")
 
 
 	// ApprovalForce forces the users to view the consent dialog
 	// ApprovalForce forces the users to view the consent dialog
 	// and confirm the permissions request at the URL returned
 	// and confirm the permissions request at the URL returned
 	// from AuthCodeURL, even if they've already done so.
 	// from AuthCodeURL, even if they've already done so.
-	ApprovalForce AuthCodeOption = setParam{"approval_prompt", "force"}
+	ApprovalForce AuthCodeOption = SetParam("approval_prompt", "force")
 )
 )
 
 
+// An AuthCodeOption is passed to Config.AuthCodeURL.
+type AuthCodeOption interface {
+	setValue(url.Values)
+}
+
 type setParam struct{ k, v string }
 type setParam struct{ k, v string }
 
 
 func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) }
 func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) }
 
 
-// An AuthCodeOption is passed to Config.AuthCodeURL.
-type AuthCodeOption interface {
-	setValue(url.Values)
+// SetParam builds an AuthCodeOption which passes key/value parameters
+// to a provider's authorization endpoint.
+func SetParam(key, value string) AuthCodeOption {
+	return setParam{key, value}
 }
 }
 
 
 // AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
 // AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
@@ -133,17 +135,37 @@ func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
 	return buf.String()
 	return buf.String()
 }
 }
 
 
+// PasswordCredentialsToken converts a resource owner username and password
+// pair into a token.
+//
+// Per the RFC, this grant type should only be used "when there is a high
+// degree of trust between the resource owner and the client (e.g., the client
+// is part of the device operating system or a highly privileged application),
+// and when other authorization grant types are not available."
+// See https://tools.ietf.org/html/rfc6749#section-4.3 for more info.
+//
+// The HTTP client to use is derived from the context.
+// If nil, http.DefaultClient is used.
+func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) {
+	return retrieveToken(ctx, c, url.Values{
+		"grant_type": {"password"},
+		"username":   {username},
+		"password":   {password},
+		"scope":      condVal(strings.Join(c.Scopes, " ")),
+	})
+}
+
 // Exchange converts an authorization code into a token.
 // Exchange converts an authorization code into a token.
 //
 //
 // It is used after a resource provider redirects the user back
 // It is used after a resource provider redirects the user back
 // to the Redirect URI (the URL obtained from AuthCodeURL).
 // to the Redirect URI (the URL obtained from AuthCodeURL).
 //
 //
-// The HTTP client to use is derived from the context. If nil,
-// http.DefaultClient is used. See the Context type's documentation.
+// The HTTP client to use is derived from the context.
+// If a client is not provided via the context, http.DefaultClient is used.
 //
 //
 // The code will be in the *http.Request.FormValue("code"). Before
 // The code will be in the *http.Request.FormValue("code"). Before
 // calling Exchange, be sure to validate FormValue("state").
 // calling Exchange, be sure to validate FormValue("state").
-func (c *Config) Exchange(ctx Context, code string) (*Token, error) {
+func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) {
 	return retrieveToken(ctx, c, url.Values{
 	return retrieveToken(ctx, c, url.Values{
 		"grant_type":   {"authorization_code"},
 		"grant_type":   {"authorization_code"},
 		"code":         {code},
 		"code":         {code},
@@ -156,7 +178,7 @@ func (c *Config) Exchange(ctx Context, code string) (*Token, error) {
 // given a Context value. If it returns an error, the search stops
 // given a Context value. If it returns an error, the search stops
 // with that error.  If it returns (nil, nil), the search continues
 // with that error.  If it returns (nil, nil), the search continues
 // down the list of registered funcs.
 // down the list of registered funcs.
-type contextClientFunc func(Context) (*http.Client, error)
+type contextClientFunc func(context.Context) (*http.Client, error)
 
 
 var contextClientFuncs []contextClientFunc
 var contextClientFuncs []contextClientFunc
 
 
@@ -164,7 +186,7 @@ func registerContextClientFunc(fn contextClientFunc) {
 	contextClientFuncs = append(contextClientFuncs, fn)
 	contextClientFuncs = append(contextClientFuncs, fn)
 }
 }
 
 
-func contextClient(ctx Context) (*http.Client, error) {
+func contextClient(ctx context.Context) (*http.Client, error) {
 	for _, fn := range contextClientFuncs {
 	for _, fn := range contextClientFuncs {
 		c, err := fn(ctx)
 		c, err := fn(ctx)
 		if err != nil {
 		if err != nil {
@@ -174,15 +196,13 @@ func contextClient(ctx Context) (*http.Client, error) {
 			return c, nil
 			return c, nil
 		}
 		}
 	}
 	}
-	if xc, ok := ctx.(context.Context); ok {
-		if hc, ok := xc.Value(HTTPClient).(*http.Client); ok {
-			return hc, nil
-		}
+	if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok {
+		return hc, nil
 	}
 	}
 	return http.DefaultClient, nil
 	return http.DefaultClient, nil
 }
 }
 
 
-func contextTransport(ctx Context) http.RoundTripper {
+func contextTransport(ctx context.Context) http.RoundTripper {
 	hc, err := contextClient(ctx)
 	hc, err := contextClient(ctx)
 	if err != nil {
 	if err != nil {
 		// This is a rare error case (somebody using nil on App Engine),
 		// This is a rare error case (somebody using nil on App Engine),
@@ -198,54 +218,64 @@ func contextTransport(ctx Context) http.RoundTripper {
 // The token will auto-refresh as necessary. The underlying
 // The token will auto-refresh as necessary. The underlying
 // HTTP transport will be obtained using the provided context.
 // HTTP transport will be obtained using the provided context.
 // The returned client and its Transport should not be modified.
 // The returned client and its Transport should not be modified.
-func (c *Config) Client(ctx Context, t *Token) *http.Client {
+func (c *Config) Client(ctx context.Context, t *Token) *http.Client {
 	return NewClient(ctx, c.TokenSource(ctx, t))
 	return NewClient(ctx, c.TokenSource(ctx, t))
 }
 }
 
 
 // TokenSource returns a TokenSource that returns t until t expires,
 // TokenSource returns a TokenSource that returns t until t expires,
 // automatically refreshing it as necessary using the provided context.
 // automatically refreshing it as necessary using the provided context.
-// See the the Context documentation.
 //
 //
 // Most users will use Config.Client instead.
 // Most users will use Config.Client instead.
-func (c *Config) TokenSource(ctx Context, t *Token) TokenSource {
-	nwn := &newWhenNeededSource{t: t}
-	nwn.new = tokenRefresher{
-		ctx:      ctx,
-		conf:     c,
-		oldToken: &nwn.t,
-	}
-	return nwn
+func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource {
+	tkr := &tokenRefresher{
+		ctx:  ctx,
+		conf: c,
+	}
+	if t != nil {
+		tkr.refreshToken = t.RefreshToken
+	}
+	return &reuseTokenSource{
+		t:   t,
+		new: tkr,
+	}
 }
 }
 
 
 // tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token"
 // tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token"
 // HTTP requests to renew a token using a RefreshToken.
 // HTTP requests to renew a token using a RefreshToken.
 type tokenRefresher struct {
 type tokenRefresher struct {
-	ctx      Context // used to get HTTP requests
-	conf     *Config
-	oldToken **Token // pointer to old *Token w/ RefreshToken
+	ctx          context.Context // used to get HTTP requests
+	conf         *Config
+	refreshToken string
 }
 }
 
 
-func (tf tokenRefresher) Token() (*Token, error) {
-	t := *tf.oldToken
-	if t == nil {
-		return nil, errors.New("oauth2: attempted use of nil Token")
-	}
-	if t.RefreshToken == "" {
+// WARNING: Token is not safe for concurrent access, as it
+// updates the tokenRefresher's refreshToken field.
+// Within this package, it is used by reuseTokenSource which
+// synchronizes calls to this method with its own mutex.
+func (tf *tokenRefresher) Token() (*Token, error) {
+	if tf.refreshToken == "" {
 		return nil, errors.New("oauth2: token expired and refresh token is not set")
 		return nil, errors.New("oauth2: token expired and refresh token is not set")
 	}
 	}
-	return retrieveToken(tf.ctx, tf.conf, url.Values{
+
+	tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{
 		"grant_type":    {"refresh_token"},
 		"grant_type":    {"refresh_token"},
-		"refresh_token": {t.RefreshToken},
+		"refresh_token": {tf.refreshToken},
 	})
 	})
+
+	if err != nil {
+		return nil, err
+	}
+	if tf.refreshToken != tk.RefreshToken {
+		tf.refreshToken = tk.RefreshToken
+	}
+	return tk, err
 }
 }
 
 
-// newWhenNeededSource is a TokenSource that holds a single token in memory
+// reuseTokenSource is a TokenSource that holds a single token in memory
 // and validates its expiry before each call to retrieve it with
 // and validates its expiry before each call to retrieve it with
 // Token. If it's expired, it will be auto-refreshed using the
 // Token. If it's expired, it will be auto-refreshed using the
 // new TokenSource.
 // new TokenSource.
-//
-// The first call to TokenRefresher must be SetToken.
-type newWhenNeededSource struct {
+type reuseTokenSource struct {
 	new TokenSource // called when t is expired.
 	new TokenSource // called when t is expired.
 
 
 	mu sync.Mutex // guards t
 	mu sync.Mutex // guards t
@@ -255,10 +285,10 @@ type newWhenNeededSource struct {
 // Token returns the current token if it's still valid, else will
 // Token returns the current token if it's still valid, else will
 // refresh the current token (using r.Context for HTTP client
 // refresh the current token (using r.Context for HTTP client
 // information) and return the new one.
 // information) and return the new one.
-func (s *newWhenNeededSource) Token() (*Token, error) {
+func (s *reuseTokenSource) Token() (*Token, error) {
 	s.mu.Lock()
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	defer s.mu.Unlock()
-	if s.t != nil && !s.t.Expired() {
+	if s.t.Valid() {
 		return s.t, nil
 		return s.t, nil
 	}
 	}
 	t, err := s.new.Token()
 	t, err := s.new.Token()
@@ -269,7 +299,7 @@ func (s *newWhenNeededSource) Token() (*Token, error) {
 	return t, nil
 	return t, nil
 }
 }
 
 
-func retrieveToken(ctx Context, c *Config, v url.Values) (*Token, error) {
+func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
 	hc, err := contextClient(ctx)
 	hc, err := contextClient(ctx)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -284,7 +314,7 @@ func retrieveToken(ctx Context, c *Config, v url.Values) (*Token, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
 	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
-	if !bustedAuth && c.ClientSecret != "" {
+	if !bustedAuth {
 		req.SetBasicAuth(c.ClientID, c.ClientSecret)
 		req.SetBasicAuth(c.ClientID, c.ClientSecret)
 	}
 	}
 	r, err := hc.Do(req)
 	r, err := hc.Do(req)
@@ -350,11 +380,11 @@ func retrieveToken(ctx Context, c *Config, v url.Values) (*Token, error) {
 // tokenJSON is the struct representing the HTTP response from OAuth2
 // tokenJSON is the struct representing the HTTP response from OAuth2
 // providers returning a token in JSON form.
 // providers returning a token in JSON form.
 type tokenJSON struct {
 type tokenJSON struct {
-	AccessToken  string `json:"access_token"`
-	TokenType    string `json:"token_type"`
-	RefreshToken string `json:"refresh_token"`
-	ExpiresIn    int32  `json:"expires_in"`
-	Expires      int32  `json:"expires"` // broken Facebook spelling of expires_in
+	AccessToken  string         `json:"access_token"`
+	TokenType    string         `json:"token_type"`
+	RefreshToken string         `json:"refresh_token"`
+	ExpiresIn    expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
+	Expires      expirationTime `json:"expires"`    // broken Facebook spelling of expires_in
 }
 }
 
 
 func (e *tokenJSON) expiry() (t time.Time) {
 func (e *tokenJSON) expiry() (t time.Time) {
@@ -367,6 +397,22 @@ func (e *tokenJSON) expiry() (t time.Time) {
 	return
 	return
 }
 }
 
 
+type expirationTime int32
+
+func (e *expirationTime) UnmarshalJSON(b []byte) error {
+	var n json.Number
+	err := json.Unmarshal(b, &n)
+	if err != nil {
+		return err
+	}
+	i, err := n.Int64()
+	if err != nil {
+		return err
+	}
+	*e = expirationTime(i)
+	return nil
+}
+
 func condVal(v string) []string {
 func condVal(v string) []string {
 	if v == "" {
 	if v == "" {
 		return nil
 		return nil
@@ -374,6 +420,25 @@ func condVal(v string) []string {
 	return []string{v}
 	return []string{v}
 }
 }
 
 
+var brokenAuthHeaderProviders = []string{
+	"https://accounts.google.com/",
+	"https://www.googleapis.com/",
+	"https://github.com/",
+	"https://api.instagram.com/",
+	"https://www.douban.com/",
+	"https://api.dropbox.com/",
+	"https://api.soundcloud.com/",
+	"https://www.linkedin.com/",
+	"https://api.twitch.tv/",
+	"https://oauth.vk.com/",
+	"https://api.odnoklassniki.ru/",
+	"https://connect.stripe.com/",
+	"https://api.pushbullet.com/",
+	"https://oauth.sandbox.trainingpeaks.com/",
+	"https://oauth.trainingpeaks.com/",
+	"https://www.strava.com/oauth/",
+}
+
 // providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
 // providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
 // implements the OAuth2 spec correctly
 // implements the OAuth2 spec correctly
 // See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
 // See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
@@ -381,16 +446,13 @@ func condVal(v string) []string {
 // - Reddit only accepts client secret in the Authorization header
 // - Reddit only accepts client secret in the Authorization header
 // - Dropbox accepts either it in URL param or Auth header, but not both.
 // - Dropbox accepts either it in URL param or Auth header, but not both.
 // - Google only accepts URL param (not spec compliant?), not Auth header
 // - Google only accepts URL param (not spec compliant?), not Auth header
+// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
 func providerAuthHeaderWorks(tokenURL string) bool {
 func providerAuthHeaderWorks(tokenURL string) bool {
-	if strings.HasPrefix(tokenURL, "https://accounts.google.com/") ||
-		strings.HasPrefix(tokenURL, "https://github.com/") ||
-		strings.HasPrefix(tokenURL, "https://api.instagram.com/") ||
-		strings.HasPrefix(tokenURL, "https://www.douban.com/") ||
-		strings.HasPrefix(tokenURL, "https://api.dropbox.com/") ||
-		strings.HasPrefix(tokenURL, "https://api.soundcloud.com/") ||
-		strings.HasPrefix(tokenURL, "https://www.linkedin.com/") {
-		// Some sites fail to implement the OAuth2 spec fully.
-		return false
+	for _, s := range brokenAuthHeaderProviders {
+		if strings.HasPrefix(tokenURL, s) {
+			// Some sites fail to implement the OAuth2 spec fully.
+			return false
+		}
 	}
 	}
 
 
 	// Assume the provider implements the spec properly
 	// Assume the provider implements the spec properly
@@ -410,12 +472,52 @@ var HTTPClient contextKey
 type contextKey struct{}
 type contextKey struct{}
 
 
 // NewClient creates an *http.Client from a Context and TokenSource.
 // NewClient creates an *http.Client from a Context and TokenSource.
-// The client's lifetime does not extend beyond the lifetime of the context.
-func NewClient(ctx Context, src TokenSource) *http.Client {
+// The returned client is not valid beyond the lifetime of the context.
+//
+// As a special case, if src is nil, a non-OAuth2 client is returned
+// using the provided context. This exists to support related OAuth2
+// packages.
+func NewClient(ctx context.Context, src TokenSource) *http.Client {
+	if src == nil {
+		c, err := contextClient(ctx)
+		if err != nil {
+			return &http.Client{Transport: errorTransport{err}}
+		}
+		return c
+	}
 	return &http.Client{
 	return &http.Client{
 		Transport: &Transport{
 		Transport: &Transport{
 			Base:   contextTransport(ctx),
 			Base:   contextTransport(ctx),
-			Source: src,
+			Source: ReuseTokenSource(nil, src),
 		},
 		},
 	}
 	}
 }
 }
+
+// ReuseTokenSource returns a TokenSource which repeatedly returns the
+// same token as long as it's valid, starting with t.
+// When its cached token is invalid, a new token is obtained from src.
+//
+// ReuseTokenSource is typically used to reuse tokens from a cache
+// (such as a file on disk) between runs of a program, rather than
+// obtaining new tokens unnecessarily.
+//
+// The initial token t may be nil, in which case the TokenSource is
+// wrapped in a caching version if it isn't one already. This also
+// means it's always safe to wrap ReuseTokenSource around any other
+// TokenSource without adverse effects.
+func ReuseTokenSource(t *Token, src TokenSource) TokenSource {
+	// Don't wrap a reuseTokenSource in itself. That would work,
+	// but cause an unnecessary number of mutex operations.
+	// Just build the equivalent one.
+	if rt, ok := src.(*reuseTokenSource); ok {
+		if t == nil {
+			// Just use it directly.
+			return rt
+		}
+		src = rt.new
+	}
+	return &reuseTokenSource{
+		t:   t,
+		new: src,
+	}
+}

+ 183 - 4
Godeps/_workspace/src/golang.org/x/oauth2/oauth2_test.go

@@ -5,11 +5,16 @@
 package oauth2
 package oauth2
 
 
 import (
 import (
+	"encoding/json"
 	"errors"
 	"errors"
+	"fmt"
 	"io/ioutil"
 	"io/ioutil"
 	"net/http"
 	"net/http"
 	"net/http/httptest"
 	"net/http/httptest"
+	"reflect"
+	"strconv"
 	"testing"
 	"testing"
+	"time"
 
 
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
@@ -56,6 +61,15 @@ func TestAuthCodeURL(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestAuthCodeURL_CustomParam(t *testing.T) {
+	conf := newConf("server")
+	param := SetParam("foo", "bar")
+	url := conf.AuthCodeURL("baz", param)
+	if url != "server/auth?client_id=CLIENT_ID&foo=bar&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=baz" {
+		t.Errorf("Auth code URL doesn't match the expected, found: %v", url)
+	}
+}
+
 func TestAuthCodeURL_Optional(t *testing.T) {
 func TestAuthCodeURL_Optional(t *testing.T) {
 	conf := &Config{
 	conf := &Config{
 		ClientID: "CLIENT_ID",
 		ClientID: "CLIENT_ID",
@@ -99,8 +113,8 @@ func TestExchangeRequest(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Error(err)
 		t.Error(err)
 	}
 	}
-	if tok.Expired() {
-		t.Errorf("Token shouldn't be expired.")
+	if !tok.Valid() {
+		t.Fatalf("Token invalid. Got: %#v", tok)
 	}
 	}
 	if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
 	if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
 		t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
 		t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
@@ -143,8 +157,8 @@ func TestExchangeRequest_JSONResponse(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Error(err)
 		t.Error(err)
 	}
 	}
-	if tok.Expired() {
-		t.Errorf("Token shouldn't be expired.")
+	if !tok.Valid() {
+		t.Fatalf("Token invalid. Got: %#v", tok)
 	}
 	}
 	if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
 	if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
 		t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
 		t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
@@ -158,6 +172,60 @@ func TestExchangeRequest_JSONResponse(t *testing.T) {
 	}
 	}
 }
 }
 
 
+const day = 24 * time.Hour
+
+func TestExchangeRequest_JSONResponse_Expiry(t *testing.T) {
+	seconds := int32(day.Seconds())
+	jsonNumberType := reflect.TypeOf(json.Number("0"))
+	for _, c := range []struct {
+		expires string
+		expect  error
+	}{
+		{fmt.Sprintf(`"expires_in": %d`, seconds), nil},
+		{fmt.Sprintf(`"expires_in": "%d"`, seconds), nil},                                             // PayPal case
+		{fmt.Sprintf(`"expires": %d`, seconds), nil},                                                  // Facebook case
+		{`"expires": false`, &json.UnmarshalTypeError{Value: "bool", Type: jsonNumberType}},           // wrong type
+		{`"expires": {}`, &json.UnmarshalTypeError{Value: "object", Type: jsonNumberType}},            // wrong type
+		{`"expires": "zzz"`, &strconv.NumError{Func: "ParseInt", Num: "zzz", Err: strconv.ErrSyntax}}, // wrong value
+	} {
+		testExchangeRequest_JSONResponse_expiry(t, c.expires, c.expect)
+	}
+}
+
+func testExchangeRequest_JSONResponse_expiry(t *testing.T, exp string, expect error) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "application/json")
+		w.Write([]byte(fmt.Sprintf(`{"access_token": "90d", "scope": "user", "token_type": "bearer", %s}`, exp)))
+	}))
+	defer ts.Close()
+	conf := newConf(ts.URL)
+	t1 := time.Now().Add(day)
+	tok, err := conf.Exchange(NoContext, "exchange-code")
+	t2 := time.Now().Add(day)
+	// Do a fmt.Sprint comparison so either side can be
+	// nil. fmt.Sprint just stringifies them to "<nil>", and no
+	// non-nil expected error ever stringifies as "<nil>", so this
+	// isn't terribly disgusting.  We do this because Go 1.4 and
+	// Go 1.5 return a different deep value for
+	// json.UnmarshalTypeError.  In Go 1.5, the
+	// json.UnmarshalTypeError contains a new field with a new
+	// non-zero value.  Rather than ignore it here with reflect or
+	// add new files and +build tags, just look at the strings.
+	if fmt.Sprint(err) != fmt.Sprint(expect) {
+		t.Errorf("Error = %v; want %v", err, expect)
+	}
+	if err != nil {
+		return
+	}
+	if !tok.Valid() {
+		t.Fatalf("Token invalid. Got: %#v", tok)
+	}
+	expiry := tok.Expiry
+	if expiry.Before(t1) || expiry.After(t2) {
+		t.Errorf("Unexpected value for Expiry: %v (shold be between %v and %v)", expiry, t1, t2)
+	}
+}
+
 func TestExchangeRequest_BadResponse(t *testing.T) {
 func TestExchangeRequest_BadResponse(t *testing.T) {
 	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "application/json")
 		w.Header().Set("Content-Type", "application/json")
@@ -210,6 +278,53 @@ func TestExchangeRequest_NonBasicAuth(t *testing.T) {
 	conf.Exchange(ctx, "code")
 	conf.Exchange(ctx, "code")
 }
 }
 
 
+func TestPasswordCredentialsTokenRequest(t *testing.T) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		expected := "/token"
+		if r.URL.String() != expected {
+			t.Errorf("URL = %q; want %q", r.URL, expected)
+		}
+		headerAuth := r.Header.Get("Authorization")
+		expected = "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ="
+		if headerAuth != expected {
+			t.Errorf("Authorization header = %q; want %q", headerAuth, expected)
+		}
+		headerContentType := r.Header.Get("Content-Type")
+		expected = "application/x-www-form-urlencoded"
+		if headerContentType != expected {
+			t.Errorf("Content-Type header = %q; want %q", headerContentType, expected)
+		}
+		body, err := ioutil.ReadAll(r.Body)
+		if err != nil {
+			t.Errorf("Failed reading request body: %s.", err)
+		}
+		expected = "client_id=CLIENT_ID&grant_type=password&password=password1&scope=scope1+scope2&username=user1"
+		if string(body) != expected {
+			t.Errorf("res.Body = %q; want %q", string(body), expected)
+		}
+		w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
+		w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer"))
+	}))
+	defer ts.Close()
+	conf := newConf(ts.URL)
+	tok, err := conf.PasswordCredentialsToken(NoContext, "user1", "password1")
+	if err != nil {
+		t.Error(err)
+	}
+	if !tok.Valid() {
+		t.Fatalf("Token invalid. Got: %#v", tok)
+	}
+	expected := "90d64460d14870c08c81352a05dedd3465940a7c"
+	if tok.AccessToken != expected {
+		t.Errorf("AccessToken = %q; want %q", tok.AccessToken, expected)
+	}
+	expected = "bearer"
+	if tok.TokenType != expected {
+		t.Errorf("TokenType = %q; want %q", tok.TokenType, expected)
+	}
+}
+
 func TestTokenRefreshRequest(t *testing.T) {
 func TestTokenRefreshRequest(t *testing.T) {
 	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		if r.URL.String() == "/somethingelse" {
 		if r.URL.String() == "/somethingelse" {
@@ -258,3 +373,67 @@ func TestFetchWithNoRefreshToken(t *testing.T) {
 		t.Errorf("Fetch should return an error if no refresh token is set")
 		t.Errorf("Fetch should return an error if no refresh token is set")
 	}
 	}
 }
 }
+
+func TestRefreshToken_RefreshTokenReplacement(t *testing.T) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "application/json")
+		w.Write([]byte(`{"access_token":"ACCESS TOKEN",  "scope": "user", "token_type": "bearer", "refresh_token": "NEW REFRESH TOKEN"}`))
+		return
+	}))
+	defer ts.Close()
+	conf := newConf(ts.URL)
+	tkr := tokenRefresher{
+		conf:         conf,
+		ctx:          NoContext,
+		refreshToken: "OLD REFRESH TOKEN",
+	}
+	tk, err := tkr.Token()
+	if err != nil {
+		t.Errorf("Unexpected refreshToken error returned: %v", err)
+		return
+	}
+	if tk.RefreshToken != tkr.refreshToken {
+		t.Errorf("tokenRefresher.refresh_token = %s; want %s", tkr.refreshToken, tk.RefreshToken)
+	}
+}
+
+func TestConfigClientWithToken(t *testing.T) {
+	tok := &Token{
+		AccessToken: "abc123",
+	}
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if got, want := r.Header.Get("Authorization"), fmt.Sprintf("Bearer %s", tok.AccessToken); got != want {
+			t.Errorf("Authorization header = %q; want %q", got, want)
+		}
+		return
+	}))
+	defer ts.Close()
+	conf := newConf(ts.URL)
+
+	c := conf.Client(NoContext, tok)
+	req, err := http.NewRequest("GET", ts.URL, nil)
+	if err != nil {
+		t.Error(err)
+	}
+	_, err = c.Do(req)
+	if err != nil {
+		t.Error(err)
+	}
+}
+
+func Test_providerAuthHeaderWorks(t *testing.T) {
+	for _, p := range brokenAuthHeaderProviders {
+		if providerAuthHeaderWorks(p) {
+			t.Errorf("URL: %s not found in list", p)
+		}
+		p := fmt.Sprintf("%ssomesuffix", p)
+		if providerAuthHeaderWorks(p) {
+			t.Errorf("URL: %s not found in list", p)
+		}
+	}
+	p := "https://api.not-in-the-list-example.com/"
+	if !providerAuthHeaderWorks(p) {
+		t.Errorf("URL: %s found in list", p)
+	}
+
+}

+ 16 - 0
Godeps/_workspace/src/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go

@@ -0,0 +1,16 @@
+// Copyright 2015 The oauth2 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 odnoklassniki provides constants for using OAuth2 to access Odnoklassniki.
+package odnoklassniki // import "golang.org/x/oauth2/odnoklassniki"
+
+import (
+	"golang.org/x/oauth2"
+)
+
+// Endpoint is Odnoklassniki's OAuth 2.0 endpoint.
+var Endpoint = oauth2.Endpoint{
+	AuthURL:  "https://www.odnoklassniki.ru/oauth/authorize",
+	TokenURL: "https://api.odnoklassniki.ru/oauth/token.do",
+}

+ 22 - 0
Godeps/_workspace/src/golang.org/x/oauth2/paypal/paypal.go

@@ -0,0 +1,22 @@
+// Copyright 2015 The oauth2 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 paypal provides constants for using OAuth2 to access PayPal.
+package paypal // import "golang.org/x/oauth2/paypal"
+
+import (
+	"golang.org/x/oauth2"
+)
+
+// Endpoint is PayPal's OAuth 2.0 endpoint in live (production) environment.
+var Endpoint = oauth2.Endpoint{
+	AuthURL:  "https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize",
+	TokenURL: "https://api.paypal.com/v1/identity/openidconnect/tokenservice",
+}
+
+// SandboxEndpoint is PayPal's OAuth 2.0 endpoint in sandbox (testing) environment.
+var SandboxEndpoint = oauth2.Endpoint{
+	AuthURL:  "https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize",
+	TokenURL: "https://api.sandbox.paypal.com/v1/identity/openidconnect/tokenservice",
+}

+ 32 - 15
Godeps/_workspace/src/golang.org/x/oauth2/token.go

@@ -10,13 +10,18 @@ import (
 	"time"
 	"time"
 )
 )
 
 
+// expiryDelta determines how earlier a token should be considered
+// expired than its actual expiration time. It is used to avoid late
+// expirations due to client-server time mismatches.
+const expiryDelta = 10 * time.Second
+
 // Token represents the crendentials used to authorize
 // Token represents the crendentials used to authorize
 // the requests to access protected resources on the OAuth 2.0
 // the requests to access protected resources on the OAuth 2.0
 // provider's backend.
 // provider's backend.
 //
 //
 // Most users of this package should not access fields of Token
 // Most users of this package should not access fields of Token
 // directly. They're exported mostly for use by related packages
 // directly. They're exported mostly for use by related packages
-// implementing derivate OAuth2 flows.
+// implementing derivative OAuth2 flows.
 type Token struct {
 type Token struct {
 	// AccessToken is the token that authorizes and authenticates
 	// AccessToken is the token that authorizes and authenticates
 	// the requests.
 	// the requests.
@@ -60,28 +65,40 @@ func (t *Token) SetAuthHeader(r *http.Request) {
 	r.Header.Set("Authorization", t.Type()+" "+t.AccessToken)
 	r.Header.Set("Authorization", t.Type()+" "+t.AccessToken)
 }
 }
 
 
-// Extra returns an extra field returned from the server during token
-// retrieval.
-func (t *Token) Extra(key string) string {
+// WithExtra returns a new Token that's a clone of t, but using the
+// provided raw extra map. This is only intended for use by packages
+// implementing derivative OAuth2 flows.
+func (t *Token) WithExtra(extra interface{}) *Token {
+	t2 := new(Token)
+	*t2 = *t
+	t2.raw = extra
+	return t2
+}
+
+// Extra returns an extra field.
+// Extra fields are key-value pairs returned by the server as a
+// part of the token retrieval response.
+func (t *Token) Extra(key string) interface{} {
 	if vals, ok := t.raw.(url.Values); ok {
 	if vals, ok := t.raw.(url.Values); ok {
+		// TODO(jbd): Cast numeric values to int64 or float64.
 		return vals.Get(key)
 		return vals.Get(key)
 	}
 	}
 	if raw, ok := t.raw.(map[string]interface{}); ok {
 	if raw, ok := t.raw.(map[string]interface{}); ok {
-		if val, ok := raw[key].(string); ok {
-			return val
-		}
+		return raw[key]
 	}
 	}
-	return ""
+	return nil
 }
 }
 
 
-// Expired returns true if there is no access token or the
-// access token is expired.
-func (t *Token) Expired() bool {
-	if t.AccessToken == "" {
-		return true
-	}
+// expired reports whether the token is expired.
+// t must be non-nil.
+func (t *Token) expired() bool {
 	if t.Expiry.IsZero() {
 	if t.Expiry.IsZero() {
 		return false
 		return false
 	}
 	}
-	return t.Expiry.Before(time.Now())
+	return t.Expiry.Add(-expiryDelta).Before(time.Now())
+}
+
+// Valid reports whether t is non-nil, has an AccessToken, and is not expired.
+func (t *Token) Valid() bool {
+	return t != nil && t.AccessToken != "" && !t.expired()
 }
 }

+ 50 - 0
Godeps/_workspace/src/golang.org/x/oauth2/token_test.go

@@ -0,0 +1,50 @@
+// Copyright 2014 The oauth2 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 oauth2
+
+import (
+	"testing"
+	"time"
+)
+
+func TestTokenExtra(t *testing.T) {
+	type testCase struct {
+		key  string
+		val  interface{}
+		want interface{}
+	}
+	const key = "extra-key"
+	cases := []testCase{
+		{key: key, val: "abc", want: "abc"},
+		{key: key, val: 123, want: 123},
+		{key: key, val: "", want: ""},
+		{key: "other-key", val: "def", want: nil},
+	}
+	for _, tc := range cases {
+		extra := make(map[string]interface{})
+		extra[tc.key] = tc.val
+		tok := &Token{raw: extra}
+		if got, want := tok.Extra(key), tc.want; got != want {
+			t.Errorf("Extra(%q) = %q; want %q", key, got, want)
+		}
+	}
+}
+
+func TestTokenExpiry(t *testing.T) {
+	now := time.Now()
+	cases := []struct {
+		name string
+		tok  *Token
+		want bool
+	}{
+		{name: "12 seconds", tok: &Token{Expiry: now.Add(12 * time.Second)}, want: false},
+		{name: "10 seconds", tok: &Token{Expiry: now.Add(expiryDelta)}, want: true},
+	}
+	for _, tc := range cases {
+		if got, want := tc.tok.expired(), tc.want; got != want {
+			t.Errorf("expired (%q) = %v; want %v", tc.name, got, want)
+		}
+	}
+}

+ 5 - 5
Godeps/_workspace/src/golang.org/x/oauth2/transport_test.go

@@ -32,10 +32,10 @@ func TestTransportTokenSource(t *testing.T) {
 	client.Get(server.URL)
 	client.Get(server.URL)
 }
 }
 
 
-func TestExpiredWithNoAccessToken(t *testing.T) {
+func TestTokenValidNoAccessToken(t *testing.T) {
 	token := &Token{}
 	token := &Token{}
-	if !token.Expired() {
-		t.Errorf("Token should be expired if no access token is provided")
+	if token.Valid() {
+		t.Errorf("Token should not be valid with no access token")
 	}
 	}
 }
 }
 
 
@@ -43,8 +43,8 @@ func TestExpiredWithExpiry(t *testing.T) {
 	token := &Token{
 	token := &Token{
 		Expiry: time.Now().Add(-5 * time.Hour),
 		Expiry: time.Now().Add(-5 * time.Hour),
 	}
 	}
-	if !token.Expired() {
-		t.Errorf("Token should be expired if no access token is provided")
+	if token.Valid() {
+		t.Errorf("Token should not be valid if it expired in the past")
 	}
 	}
 }
 }
 
 

+ 16 - 0
Godeps/_workspace/src/golang.org/x/oauth2/vk/vk.go

@@ -0,0 +1,16 @@
+// Copyright 2015 The oauth2 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 vk provides constants for using OAuth2 to access VK.com.
+package vk // import "golang.org/x/oauth2/vk"
+
+import (
+	"golang.org/x/oauth2"
+)
+
+// Endpoint is VK's OAuth 2.0 endpoint.
+var Endpoint = oauth2.Endpoint{
+	AuthURL:  "https://oauth.vk.com/authorize",
+	TokenURL: "https://oauth.vk.com/access_token",
+}

+ 2 - 1
pkg/social/social.go

@@ -7,6 +7,7 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
+	"golang.org/x/net/context"
 
 
 	"golang.org/x/oauth2"
 	"golang.org/x/oauth2"
 )
 )
@@ -24,7 +25,7 @@ type SocialConnector interface {
 	UserInfo(token *oauth2.Token) (*BasicUserInfo, error)
 	UserInfo(token *oauth2.Token) (*BasicUserInfo, error)
 
 
 	AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string
 	AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string
-	Exchange(ctx oauth2.Context, code string) (*oauth2.Token, error)
+	Exchange(ctx context.Context, code string) (*oauth2.Token, error)
 }
 }
 
 
 var (
 var (