diff options
Diffstat (limited to 'vendor/golang.org/x/oauth2')
22 files changed, 2163 insertions, 0 deletions
diff --git a/vendor/golang.org/x/oauth2/AUTHORS b/vendor/golang.org/x/oauth2/AUTHORS new file mode 100644 index 0000000..15167cd --- /dev/null +++ b/vendor/golang.org/x/oauth2/AUTHORS @@ -0,0 +1,3 @@ +# This source code refers to The Go Authors for copyright purposes. +# The master list of authors is in the main Go distribution, +# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/oauth2/CONTRIBUTING.md b/vendor/golang.org/x/oauth2/CONTRIBUTING.md new file mode 100644 index 0000000..46aa2b1 --- /dev/null +++ b/vendor/golang.org/x/oauth2/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contributing to Go + +Go is an open source project. + +It is the work of hundreds of contributors. We appreciate your help! + + +## Filing issues + +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. + diff --git a/vendor/golang.org/x/oauth2/CONTRIBUTORS b/vendor/golang.org/x/oauth2/CONTRIBUTORS new file mode 100644 index 0000000..1c4577e --- /dev/null +++ b/vendor/golang.org/x/oauth2/CONTRIBUTORS @@ -0,0 +1,3 @@ +# This source code was written by the Go contributors. +# The master list of contributors is in the main Go distribution, +# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/oauth2/LICENSE b/vendor/golang.org/x/oauth2/LICENSE new file mode 100644 index 0000000..d02f24f --- /dev/null +++ b/vendor/golang.org/x/oauth2/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The oauth2 Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +   * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +   * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. +   * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/oauth2/README.md b/vendor/golang.org/x/oauth2/README.md new file mode 100644 index 0000000..0d51417 --- /dev/null +++ b/vendor/golang.org/x/oauth2/README.md @@ -0,0 +1,64 @@ +# OAuth2 for Go + +[](https://travis-ci.org/golang/oauth2) + +oauth2 package contains a client implementation for OAuth 2.0 spec. + +## Installation + +~~~~ +go get golang.org/x/oauth2 +~~~~ + +See godoc for further documentation and examples. + +* [godoc.org/golang.org/x/oauth2](http://godoc.org/golang.org/x/oauth2) +* [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" +		newurlfetch "google.golang.org/appengine/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("...") +	} + diff --git a/vendor/golang.org/x/oauth2/client_appengine.go b/vendor/golang.org/x/oauth2/client_appengine.go new file mode 100644 index 0000000..8962c49 --- /dev/null +++ b/vendor/golang.org/x/oauth2/client_appengine.go @@ -0,0 +1,25 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine + +// App Engine hooks. + +package oauth2 + +import ( +	"net/http" + +	"golang.org/x/net/context" +	"golang.org/x/oauth2/internal" +	"google.golang.org/appengine/urlfetch" +) + +func init() { +	internal.RegisterContextClientFunc(contextClientAppEngine) +} + +func contextClientAppEngine(ctx context.Context) (*http.Client, error) { +	return urlfetch.Client(ctx), nil +} diff --git a/vendor/golang.org/x/oauth2/github/github.go b/vendor/golang.org/x/oauth2/github/github.go new file mode 100644 index 0000000..f297801 --- /dev/null +++ b/vendor/golang.org/x/oauth2/github/github.go @@ -0,0 +1,16 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package github provides constants for using OAuth2 to access Github. +package github // import "golang.org/x/oauth2/github" + +import ( +	"golang.org/x/oauth2" +) + +// Endpoint is Github's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ +	AuthURL:  "https://github.com/login/oauth/authorize", +	TokenURL: "https://github.com/login/oauth/access_token", +} diff --git a/vendor/golang.org/x/oauth2/google/appengine.go b/vendor/golang.org/x/oauth2/google/appengine.go new file mode 100644 index 0000000..dc993ef --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/appengine.go @@ -0,0 +1,86 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( +	"sort" +	"strings" +	"sync" +	"time" + +	"golang.org/x/net/context" +	"golang.org/x/oauth2" +) + +// Set at init time by appenginevm_hook.go. If true, we are on App Engine Managed VMs. +var appengineVM bool + +// 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 +// 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. +// +// 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{ +		ctx:    ctx, +		scopes: scopes, +		key:    strings.Join(scopes, " "), +	} +} + +// 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 { +		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 tok.t, nil +} diff --git a/vendor/golang.org/x/oauth2/google/appengine_hook.go b/vendor/golang.org/x/oauth2/google/appengine_hook.go new file mode 100644 index 0000000..4f42c8b --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/appengine_hook.go @@ -0,0 +1,13 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine + +package google + +import "google.golang.org/appengine" + +func init() { +	appengineTokenFunc = appengine.AccessToken +} diff --git a/vendor/golang.org/x/oauth2/google/appenginevm_hook.go b/vendor/golang.org/x/oauth2/google/appenginevm_hook.go new file mode 100644 index 0000000..633611c --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/appenginevm_hook.go @@ -0,0 +1,14 @@ +// 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 appenginevm + +package google + +import "google.golang.org/appengine" + +func init() { +	appengineVM = true +	appengineTokenFunc = appengine.AccessToken +} diff --git a/vendor/golang.org/x/oauth2/google/default.go b/vendor/golang.org/x/oauth2/google/default.go new file mode 100644 index 0000000..b952362 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/default.go @@ -0,0 +1,155 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( +	"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/docs/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 and Google App Engine Managed VMs, 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/docs/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 && !appengineVM { +		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/docs/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) +	} +} diff --git a/vendor/golang.org/x/oauth2/google/google.go b/vendor/golang.org/x/oauth2/google/google.go new file mode 100644 index 0000000..464c75a --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/google.go @@ -0,0 +1,146 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package google 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 +// https://developers.google.com/accounts/docs/OAuth2 +// and +// https://developers.google.com/accounts/docs/application-default-credentials. +package google // import "golang.org/x/oauth2/google" + +import ( +	"encoding/json" +	"errors" +	"fmt" +	"strings" +	"time" + +	"golang.org/x/oauth2" +	"golang.org/x/oauth2/jwt" +	"google.golang.org/cloud/compute/metadata" +) + +// Endpoint is Google's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ +	AuthURL:  "https://accounts.google.com/o/oauth2/auth", +	TokenURL: "https://accounts.google.com/o/oauth2/token", +} + +// JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow. +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 downloaded from +// https://console.developers.google.com, under "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 +// the credentials that authorize and authenticate the requests. +// Create a service account on "Credentials" for your project at +// https://console.developers.google.com to download a JSON key file. +func JWTConfigFromJSON(jsonKey []byte, scope ...string) (*jwt.Config, error) { +	var key struct { +		Email      string `json:"client_email"` +		PrivateKey string `json:"private_key"` +	} +	if err := json.Unmarshal(jsonKey, &key); err != nil { +		return nil, err +	} +	return &jwt.Config{ +		Email:      key.Email, +		PrivateKey: []byte(key.PrivateKey), +		Scopes:     scope, +		TokenURL:   JWTTokenURL, +	}, nil +} + +// ComputeTokenSource returns a token source that fetches access tokens +// 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. +// If no account is specified, "default" is used. +// Further information about retrieving access tokens from the GCE metadata +// server can be found at https://cloud.google.com/compute/docs/authentication. +func ComputeTokenSource(account string) oauth2.TokenSource { +	return oauth2.ReuseTokenSource(nil, computeSource{account: account}) +} + +type computeSource struct { +	account string +} + +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 +	if acct == "" { +		acct = "default" +	} +	tokenJSON, err := metadata.Get("instance/service-accounts/" + acct + "/token") +	if err != nil { +		return nil, err +	} +	var res struct { +		AccessToken  string `json:"access_token"` +		ExpiresInSec int    `json:"expires_in"` +		TokenType    string `json:"token_type"` +	} +	err = json.NewDecoder(strings.NewReader(tokenJSON)).Decode(&res) +	if err != nil { +		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{ +		AccessToken: res.AccessToken, +		TokenType:   res.TokenType, +		Expiry:      time.Now().Add(time.Duration(res.ExpiresInSec) * time.Second), +	}, nil +} diff --git a/vendor/golang.org/x/oauth2/google/jwt.go b/vendor/golang.org/x/oauth2/google/jwt.go new file mode 100644 index 0000000..b919917 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/jwt.go @@ -0,0 +1,71 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( +	"crypto/rsa" +	"fmt" +	"time" + +	"golang.org/x/oauth2" +	"golang.org/x/oauth2/internal" +	"golang.org/x/oauth2/jws" +) + +// JWTAccessTokenSourceFromJSON uses a Google Developers service account JSON +// key file to read the credentials that authorize and authenticate the +// requests, and returns a TokenSource that does not use any OAuth2 flow but +// instead creates a JWT and sends that as the access token. +// The audience is typically a URL that specifies the scope of the credentials. +// +// Note that this is not a standard OAuth flow, but rather an +// optimization supported by a few Google services. +// Unless you know otherwise, you should use JWTConfigFromJSON instead. +func JWTAccessTokenSourceFromJSON(jsonKey []byte, audience string) (oauth2.TokenSource, error) { +	cfg, err := JWTConfigFromJSON(jsonKey) +	if err != nil { +		return nil, fmt.Errorf("google: could not parse JSON key: %v", err) +	} +	pk, err := internal.ParseKey(cfg.PrivateKey) +	if err != nil { +		return nil, fmt.Errorf("google: could not parse key: %v", err) +	} +	ts := &jwtAccessTokenSource{ +		email:    cfg.Email, +		audience: audience, +		pk:       pk, +	} +	tok, err := ts.Token() +	if err != nil { +		return nil, err +	} +	return oauth2.ReuseTokenSource(tok, ts), nil +} + +type jwtAccessTokenSource struct { +	email, audience string +	pk              *rsa.PrivateKey +} + +func (ts *jwtAccessTokenSource) Token() (*oauth2.Token, error) { +	iat := time.Now() +	exp := iat.Add(time.Hour) +	cs := &jws.ClaimSet{ +		Iss: ts.email, +		Sub: ts.email, +		Aud: ts.audience, +		Iat: iat.Unix(), +		Exp: exp.Unix(), +	} +	hdr := &jws.Header{ +		Algorithm: "RS256", +		Typ:       "JWT", +	} +	msg, err := jws.Encode(hdr, cs, ts.pk) +	if err != nil { +		return nil, fmt.Errorf("google: could not encode JWT: %v", err) +	} +	return &oauth2.Token{AccessToken: msg, TokenType: "Bearer", Expiry: exp}, nil +} diff --git a/vendor/golang.org/x/oauth2/google/sdk.go b/vendor/golang.org/x/oauth2/google/sdk.go new file mode 100644 index 0000000..d29a3bb --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/sdk.go @@ -0,0 +1,168 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( +	"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") +} diff --git a/vendor/golang.org/x/oauth2/internal/oauth2.go b/vendor/golang.org/x/oauth2/internal/oauth2.go new file mode 100644 index 0000000..fbe1028 --- /dev/null +++ b/vendor/golang.org/x/oauth2/internal/oauth2.go @@ -0,0 +1,76 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package internal contains support packages for oauth2 package. +package internal + +import ( +	"bufio" +	"crypto/rsa" +	"crypto/x509" +	"encoding/pem" +	"errors" +	"fmt" +	"io" +	"strings" +) + +// ParseKey converts the binary contents of a private key file +// to an *rsa.PrivateKey. It detects whether the private key is in a +// PEM container or not. If so, it extracts the the private key +// from PEM container before conversion. It only supports PEM +// containers with no passphrase. +func ParseKey(key []byte) (*rsa.PrivateKey, error) { +	block, _ := pem.Decode(key) +	if block != nil { +		key = block.Bytes +	} +	parsedKey, err := x509.ParsePKCS8PrivateKey(key) +	if err != nil { +		parsedKey, err = x509.ParsePKCS1PrivateKey(key) +		if err != nil { +			return nil, fmt.Errorf("private key should be a PEM or plain PKSC1 or PKCS8; parse error: %v", err) +		} +	} +	parsed, ok := parsedKey.(*rsa.PrivateKey) +	if !ok { +		return nil, errors.New("private key is invalid") +	} +	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 +} + +func CondVal(v string) []string { +	if v == "" { +		return nil +	} +	return []string{v} +} diff --git a/vendor/golang.org/x/oauth2/internal/token.go b/vendor/golang.org/x/oauth2/internal/token.go new file mode 100644 index 0000000..a6ed3cc --- /dev/null +++ b/vendor/golang.org/x/oauth2/internal/token.go @@ -0,0 +1,225 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package internal contains support packages for oauth2 package. +package internal + +import ( +	"encoding/json" +	"fmt" +	"io" +	"io/ioutil" +	"mime" +	"net/http" +	"net/url" +	"strconv" +	"strings" +	"time" + +	"golang.org/x/net/context" +) + +// Token represents the crendentials used to authorize +// the requests to access protected resources on the OAuth 2.0 +// provider's backend. +// +// This type is a mirror of oauth2.Token and exists to break +// an otherwise-circular dependency. Other internal packages +// should convert this Token into an oauth2.Token before use. +type Token struct { +	// AccessToken is the token that authorizes and authenticates +	// the requests. +	AccessToken string + +	// TokenType is the type of token. +	// The Type method returns either this or "Bearer", the default. +	TokenType string + +	// RefreshToken is a token that's used by the application +	// (as opposed to the user) to refresh the access token +	// if it expires. +	RefreshToken string + +	// Expiry is the optional expiration time of the access token. +	// +	// If zero, TokenSource implementations will reuse the same +	// token forever and RefreshToken or equivalent +	// mechanisms for that TokenSource will not be used. +	Expiry time.Time + +	// Raw optionally contains extra metadata from the server +	// when updating a token. +	Raw interface{} +} + +// tokenJSON is the struct representing the HTTP response from OAuth2 +// providers returning a token in JSON form. +type tokenJSON struct { +	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) { +	if v := e.ExpiresIn; v != 0 { +		return time.Now().Add(time.Duration(v) * time.Second) +	} +	if v := e.Expires; v != 0 { +		return time.Now().Add(time.Duration(v) * time.Second) +	} +	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 +} + +var brokenAuthHeaderProviders = []string{ +	"https://accounts.google.com/", +	"https://api.dropbox.com/", +	"https://api.dropboxapi.com/", +	"https://api.instagram.com/", +	"https://api.netatmo.net/", +	"https://api.odnoklassniki.ru/", +	"https://api.pushbullet.com/", +	"https://api.soundcloud.com/", +	"https://api.twitch.tv/", +	"https://app.box.com/", +	"https://connect.stripe.com/", +	"https://login.microsoftonline.com/", +	"https://login.salesforce.com/", +	"https://oauth.sandbox.trainingpeaks.com/", +	"https://oauth.trainingpeaks.com/", +	"https://oauth.vk.com/", +	"https://openapi.baidu.com/", +	"https://slack.com/", +	"https://test-sandbox.auth.corp.google.com", +	"https://test.salesforce.com/", +	"https://user.gini.net/", +	"https://www.douban.com/", +	"https://www.googleapis.com/", +	"https://www.linkedin.com/", +	"https://www.strava.com/oauth/", +	"https://www.wunderlist.com/oauth/", +	"https://api.patreon.com/", +} + +func RegisterBrokenAuthHeaderProvider(tokenURL string) { +	brokenAuthHeaderProviders = append(brokenAuthHeaderProviders, tokenURL) +} + +// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL +// implements the OAuth2 spec correctly +// See https://code.google.com/p/goauth2/issues/detail?id=31 for background. +// In summary: +// - Reddit only accepts client secret in the Authorization header +// - Dropbox accepts either it in URL param or Auth header, but not both. +// - 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 { +	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 +	// otherwise. We can add more exceptions as they're +	// discovered. We will _not_ be adding configurable hooks +	// to this package to let users select server bugs. +	return true +} + +func RetrieveToken(ctx context.Context, ClientID, ClientSecret, TokenURL string, v url.Values) (*Token, error) { +	hc, err := ContextClient(ctx) +	if err != nil { +		return nil, err +	} +	v.Set("client_id", ClientID) +	bustedAuth := !providerAuthHeaderWorks(TokenURL) +	if bustedAuth && ClientSecret != "" { +		v.Set("client_secret", ClientSecret) +	} +	req, err := http.NewRequest("POST", TokenURL, strings.NewReader(v.Encode())) +	if err != nil { +		return nil, err +	} +	req.Header.Set("Content-Type", "application/x-www-form-urlencoded") +	if !bustedAuth { +		req.SetBasicAuth(ClientID, ClientSecret) +	} +	r, err := hc.Do(req) +	if err != nil { +		return nil, err +	} +	defer r.Body.Close() +	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20)) +	if err != nil { +		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) +	} +	if code := r.StatusCode; code < 200 || code > 299 { +		return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body) +	} + +	var token *Token +	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type")) +	switch content { +	case "application/x-www-form-urlencoded", "text/plain": +		vals, err := url.ParseQuery(string(body)) +		if err != nil { +			return nil, err +		} +		token = &Token{ +			AccessToken:  vals.Get("access_token"), +			TokenType:    vals.Get("token_type"), +			RefreshToken: vals.Get("refresh_token"), +			Raw:          vals, +		} +		e := vals.Get("expires_in") +		if e == "" { +			// TODO(jbd): Facebook's OAuth2 implementation is broken and +			// returns expires_in field in expires. Remove the fallback to expires, +			// when Facebook fixes their implementation. +			e = vals.Get("expires") +		} +		expires, _ := strconv.Atoi(e) +		if expires != 0 { +			token.Expiry = time.Now().Add(time.Duration(expires) * time.Second) +		} +	default: +		var tj tokenJSON +		if err = json.Unmarshal(body, &tj); err != nil { +			return nil, err +		} +		token = &Token{ +			AccessToken:  tj.AccessToken, +			TokenType:    tj.TokenType, +			RefreshToken: tj.RefreshToken, +			Expiry:       tj.expiry(), +			Raw:          make(map[string]interface{}), +		} +		json.Unmarshal(body, &token.Raw) // no error checks for optional fields +	} +	// Don't overwrite `RefreshToken` with an empty value +	// if this was a token refreshing request. +	if token.RefreshToken == "" { +		token.RefreshToken = v.Get("refresh_token") +	} +	return token, nil +} diff --git a/vendor/golang.org/x/oauth2/internal/transport.go b/vendor/golang.org/x/oauth2/internal/transport.go new file mode 100644 index 0000000..f1f173e --- /dev/null +++ b/vendor/golang.org/x/oauth2/internal/transport.go @@ -0,0 +1,69 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package internal contains support packages for oauth2 package. +package internal + +import ( +	"net/http" + +	"golang.org/x/net/context" +) + +// HTTPClient is the context key to use with golang.org/x/net/context's +// WithValue function to associate an *http.Client value with a context. +var HTTPClient ContextKey + +// ContextKey is just an empty struct. It exists so HTTPClient can be +// an immutable public variable with a unique type. It's immutable +// because nobody else can create a ContextKey, being unexported. +type ContextKey struct{} + +// ContextClientFunc is a func which tries to return an *http.Client +// given a Context value. If it returns an error, the search stops +// with that error.  If it returns (nil, nil), the search continues +// down the list of registered funcs. +type ContextClientFunc func(context.Context) (*http.Client, error) + +var contextClientFuncs []ContextClientFunc + +func RegisterContextClientFunc(fn ContextClientFunc) { +	contextClientFuncs = append(contextClientFuncs, fn) +} + +func ContextClient(ctx context.Context) (*http.Client, error) { +	if ctx != nil { +		if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok { +			return hc, nil +		} +	} +	for _, fn := range contextClientFuncs { +		c, err := fn(ctx) +		if err != nil { +			return nil, err +		} +		if c != nil { +			return c, nil +		} +	} +	return http.DefaultClient, nil +} + +func ContextTransport(ctx context.Context) http.RoundTripper { +	hc, err := ContextClient(ctx) +	// This is a rare error case (somebody using nil on App Engine). +	if err != nil { +		return ErrorTransport{err} +	} +	return hc.Transport +} + +// ErrorTransport returns the specified error on RoundTrip. +// This RoundTripper should be used in rare error cases where +// error handling can be postponed to response handling time. +type ErrorTransport struct{ Err error } + +func (t ErrorTransport) RoundTrip(*http.Request) (*http.Response, error) { +	return nil, t.Err +} diff --git a/vendor/golang.org/x/oauth2/jws/jws.go b/vendor/golang.org/x/oauth2/jws/jws.go new file mode 100644 index 0000000..29887ea --- /dev/null +++ b/vendor/golang.org/x/oauth2/jws/jws.go @@ -0,0 +1,191 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package jws provides encoding and decoding utilities for +// signed JWS messages. +package jws // import "golang.org/x/oauth2/jws" + +import ( +	"bytes" +	"crypto" +	"crypto/rand" +	"crypto/rsa" +	"crypto/sha256" +	"encoding/base64" +	"encoding/json" +	"errors" +	"fmt" +	"strings" +	"time" +) + +// ClaimSet contains information about the JWT signature including the +// permissions being requested (scopes), the target of the token, the issuer, +// the time the token was issued, and the lifetime of the token. +type ClaimSet struct { +	Iss   string `json:"iss"`             // email address of the client_id of the application making the access token request +	Scope string `json:"scope,omitempty"` // space-delimited list of the permissions the application requests +	Aud   string `json:"aud"`             // descriptor of the intended target of the assertion (Optional). +	Exp   int64  `json:"exp"`             // the expiration time of the assertion (seconds since Unix epoch) +	Iat   int64  `json:"iat"`             // the time the assertion was issued (seconds since Unix epoch) +	Typ   string `json:"typ,omitempty"`   // token type (Optional). + +	// Email for which the application is requesting delegated access (Optional). +	Sub string `json:"sub,omitempty"` + +	// The old name of Sub. Client keeps setting Prn to be +	// complaint with legacy OAuth 2.0 providers. (Optional) +	Prn string `json:"prn,omitempty"` + +	// See http://tools.ietf.org/html/draft-jones-json-web-token-10#section-4.3 +	// This array is marshalled using custom code (see (c *ClaimSet) encode()). +	PrivateClaims map[string]interface{} `json:"-"` +} + +func (c *ClaimSet) encode() (string, error) { +	// Reverting time back for machines whose time is not perfectly in sync. +	// If client machine's time is in the future according +	// to Google servers, an access token will not be issued. +	now := time.Now().Add(-10 * time.Second) +	if c.Iat == 0 { +		c.Iat = now.Unix() +	} +	if c.Exp == 0 { +		c.Exp = now.Add(time.Hour).Unix() +	} +	if c.Exp < c.Iat { +		return "", fmt.Errorf("jws: invalid Exp = %v; must be later than Iat = %v", c.Exp, c.Iat) +	} + +	b, err := json.Marshal(c) +	if err != nil { +		return "", err +	} + +	if len(c.PrivateClaims) == 0 { +		return base64Encode(b), nil +	} + +	// Marshal private claim set and then append it to b. +	prv, err := json.Marshal(c.PrivateClaims) +	if err != nil { +		return "", fmt.Errorf("jws: invalid map of private claims %v", c.PrivateClaims) +	} + +	// Concatenate public and private claim JSON objects. +	if !bytes.HasSuffix(b, []byte{'}'}) { +		return "", fmt.Errorf("jws: invalid JSON %s", b) +	} +	if !bytes.HasPrefix(prv, []byte{'{'}) { +		return "", fmt.Errorf("jws: invalid JSON %s", prv) +	} +	b[len(b)-1] = ','         // Replace closing curly brace with a comma. +	b = append(b, prv[1:]...) // Append private claims. +	return base64Encode(b), nil +} + +// Header represents the header for the signed JWS payloads. +type Header struct { +	// The algorithm used for signature. +	Algorithm string `json:"alg"` + +	// Represents the token type. +	Typ string `json:"typ"` +} + +func (h *Header) encode() (string, error) { +	b, err := json.Marshal(h) +	if err != nil { +		return "", err +	} +	return base64Encode(b), nil +} + +// Decode decodes a claim set from a JWS payload. +func Decode(payload string) (*ClaimSet, error) { +	// decode returned id token to get expiry +	s := strings.Split(payload, ".") +	if len(s) < 2 { +		// TODO(jbd): Provide more context about the error. +		return nil, errors.New("jws: invalid token received") +	} +	decoded, err := base64Decode(s[1]) +	if err != nil { +		return nil, err +	} +	c := &ClaimSet{} +	err = json.NewDecoder(bytes.NewBuffer(decoded)).Decode(c) +	return c, err +} + +// Signer returns a signature for the given data. +type Signer func(data []byte) (sig []byte, err error) + +// EncodeWithSigner encodes a header and claim set with the provided signer. +func EncodeWithSigner(header *Header, c *ClaimSet, sg Signer) (string, error) { +	head, err := header.encode() +	if err != nil { +		return "", err +	} +	cs, err := c.encode() +	if err != nil { +		return "", err +	} +	ss := fmt.Sprintf("%s.%s", head, cs) +	sig, err := sg([]byte(ss)) +	if err != nil { +		return "", err +	} +	return fmt.Sprintf("%s.%s", ss, base64Encode(sig)), nil +} + +// Encode encodes a signed JWS with provided header and claim set. +// This invokes EncodeWithSigner using crypto/rsa.SignPKCS1v15 with the given RSA private key. +func Encode(header *Header, c *ClaimSet, key *rsa.PrivateKey) (string, error) { +	sg := func(data []byte) (sig []byte, err error) { +		h := sha256.New() +		h.Write(data) +		return rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, h.Sum(nil)) +	} +	return EncodeWithSigner(header, c, sg) +} + +// Verify tests whether the provided JWT token's signature was produced by the private key +// associated with the supplied public key. +func Verify(token string, key *rsa.PublicKey) error { +	parts := strings.Split(token, ".") +	if len(parts) != 3 { +		return errors.New("jws: invalid token received, token must have 3 parts") +	} + +	signedContent := parts[0] + "." + parts[1] +	signatureString, err := base64Decode(parts[2]) +	if err != nil { +		return err +	} + +	h := sha256.New() +	h.Write([]byte(signedContent)) +	return rsa.VerifyPKCS1v15(key, crypto.SHA256, h.Sum(nil), []byte(signatureString)) +} + +// base64Encode returns and Base64url encoded version of the input string with any +// trailing "=" stripped. +func base64Encode(b []byte) string { +	return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=") +} + +// base64Decode decodes the Base64url encoded string +func base64Decode(s string) ([]byte, error) { +	// add back missing padding +	switch len(s) % 4 { +	case 1: +		s += "===" +	case 2: +		s += "==" +	case 3: +		s += "=" +	} +	return base64.URLEncoding.DecodeString(s) +} diff --git a/vendor/golang.org/x/oauth2/jwt/jwt.go b/vendor/golang.org/x/oauth2/jwt/jwt.go new file mode 100644 index 0000000..2ffad21 --- /dev/null +++ b/vendor/golang.org/x/oauth2/jwt/jwt.go @@ -0,0 +1,153 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package 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 ( +	"encoding/json" +	"fmt" +	"io" +	"io/ioutil" +	"net/http" +	"net/url" +	"strings" +	"time" + +	"golang.org/x/net/context" +	"golang.org/x/oauth2" +	"golang.org/x/oauth2/internal" +	"golang.org/x/oauth2/jws" +) + +var ( +	defaultGrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer" +	defaultHeader    = &jws.Header{Algorithm: "RS256", Typ: "JWT"} +) + +// 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 +	// the configured OAuth provider. +	Email string + +	// PrivateKey contains the contents of an RSA private key or the +	// contents of a PEM file that contains a private key. The provided +	// private key is used to sign JWT payloads. +	// PEM containers with a passphrase are not supported. +	// Use the following command to convert a PKCS 12 file into a PEM. +	// +	//    $ openssl pkcs12 -in key.p12 -out key.pem -nodes +	// +	PrivateKey []byte + +	// Subject is the optional user to impersonate. +	Subject string + +	// Scopes optionally specifies a list of requested permission scopes. +	Scopes []string + +	// TokenURL is the endpoint required to complete the 2-legged JWT flow. +	TokenURL string + +	// Expires optionally specifies how long the token is valid for. +	Expires time.Duration +} + +// TokenSource returns a JWT TokenSource using the configuration +// in c and the HTTP client from the provided context. +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 +// HTTP transport and adding Authorization headers with tokens +// obtained from c. +// +// The returned client and its Transport should not be modified. +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. +// It should typically be wrapped with a reuseTokenSource. +type jwtSource struct { +	ctx  context.Context +	conf *Config +} + +func (js jwtSource) Token() (*oauth2.Token, error) { +	pk, err := internal.ParseKey(js.conf.PrivateKey) +	if err != nil { +		return nil, err +	} +	hc := oauth2.NewClient(js.ctx, nil) +	claimSet := &jws.ClaimSet{ +		Iss:   js.conf.Email, +		Scope: strings.Join(js.conf.Scopes, " "), +		Aud:   js.conf.TokenURL, +	} +	if subject := js.conf.Subject; subject != "" { +		claimSet.Sub = subject +		// prn is the old name of sub. Keep setting it +		// to be compatible with legacy OAuth 2.0 providers. +		claimSet.Prn = subject +	} +	if t := js.conf.Expires; t > 0 { +		claimSet.Exp = time.Now().Add(t).Unix() +	} +	payload, err := jws.Encode(defaultHeader, claimSet, pk) +	if err != nil { +		return nil, err +	} +	v := url.Values{} +	v.Set("grant_type", defaultGrantType) +	v.Set("assertion", payload) +	resp, err := hc.PostForm(js.conf.TokenURL, v) +	if err != nil { +		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) +	} +	defer resp.Body.Close() +	body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) +	if err != nil { +		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) +	} +	if c := resp.StatusCode; c < 200 || c > 299 { +		return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", resp.Status, body) +	} +	// tokenRes is the JSON response body. +	var tokenRes struct { +		AccessToken string `json:"access_token"` +		TokenType   string `json:"token_type"` +		IDToken     string `json:"id_token"` +		ExpiresIn   int64  `json:"expires_in"` // relative seconds from now +	} +	if err := json.Unmarshal(body, &tokenRes); err != nil { +		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) +	} +	token := &oauth2.Token{ +		AccessToken: tokenRes.AccessToken, +		TokenType:   tokenRes.TokenType, +	} +	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 { +		token.Expiry = time.Now().Add(time.Duration(secs) * time.Second) +	} +	if v := tokenRes.IDToken; v != "" { +		// decode returned id token to get expiry +		claimSet, err := jws.Decode(v) +		if err != nil { +			return nil, fmt.Errorf("oauth2: error decoding JWT token: %v", err) +		} +		token.Expiry = time.Unix(claimSet.Exp, 0) +	} +	return token, nil +} diff --git a/vendor/golang.org/x/oauth2/oauth2.go b/vendor/golang.org/x/oauth2/oauth2.go new file mode 100644 index 0000000..a682896 --- /dev/null +++ b/vendor/golang.org/x/oauth2/oauth2.go @@ -0,0 +1,337 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package oauth2 provides support for making +// OAuth2 authorized and authenticated HTTP requests. +// It can additionally grant authorization with Bearer JWT. +package oauth2 // import "golang.org/x/oauth2" + +import ( +	"bytes" +	"errors" +	"net/http" +	"net/url" +	"strings" +	"sync" + +	"golang.org/x/net/context" +	"golang.org/x/oauth2/internal" +) + +// 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() + +// RegisterBrokenAuthHeaderProvider registers an OAuth2 server +// identified by the tokenURL prefix as an OAuth2 implementation +// which doesn't support the HTTP Basic authentication +// scheme to authenticate with the authorization server. +// Once a server is registered, credentials (client_id and client_secret) +// will be passed as query parameters rather than being present +// in the Authorization header. +// See https://code.google.com/p/goauth2/issues/detail?id=31 for background. +func RegisterBrokenAuthHeaderProvider(tokenURL string) { +	internal.RegisterBrokenAuthHeaderProvider(tokenURL) +} + +// Config describes a typical 3-legged OAuth2 flow, with both the +// client application information and the server's endpoint URLs. +type Config struct { +	// ClientID is the application's ID. +	ClientID string + +	// ClientSecret is the application's secret. +	ClientSecret string + +	// Endpoint contains the resource server's token 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 + +	// RedirectURL is the URL to redirect users going through +	// the OAuth flow, after the resource owner's URLs. +	RedirectURL string + +	// Scope specifies optional requested permissions. +	Scopes []string +} + +// A TokenSource is anything that can return a token. +type TokenSource interface { +	// 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) +} + +// Endpoint contains the OAuth 2.0 provider's authorization and token +// endpoint URLs. +type Endpoint struct { +	AuthURL  string +	TokenURL string +} + +var ( +	// AccessTypeOnline and AccessTypeOffline are options passed +	// to the Options.AuthCodeURL method. They modify the +	// "access_type" field that gets sent in the URL returned by +	// AuthCodeURL. +	// +	// 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. +	AccessTypeOnline  AuthCodeOption = SetAuthURLParam("access_type", "online") +	AccessTypeOffline AuthCodeOption = SetAuthURLParam("access_type", "offline") + +	// ApprovalForce forces the users to view the consent dialog +	// and confirm the permissions request at the URL returned +	// from AuthCodeURL, even if they've already done so. +	ApprovalForce AuthCodeOption = SetAuthURLParam("approval_prompt", "force") +) + +// An AuthCodeOption is passed to Config.AuthCodeURL. +type AuthCodeOption interface { +	setValue(url.Values) +} + +type setParam struct{ k, v string } + +func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) } + +// SetAuthURLParam builds an AuthCodeOption which passes key/value parameters +// to a provider's authorization endpoint. +func SetAuthURLParam(key, value string) AuthCodeOption { +	return setParam{key, value} +} + +// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page +// that asks for permissions for the required scopes explicitly. +// +// State is a token to protect the user from CSRF attacks. You must +// always provide a non-zero string and validate that it matches the +// the state query parameter on your redirect callback. +// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info. +// +// Opts may include AccessTypeOnline or AccessTypeOffline, as well +// as ApprovalForce. +func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string { +	var buf bytes.Buffer +	buf.WriteString(c.Endpoint.AuthURL) +	v := url.Values{ +		"response_type": {"code"}, +		"client_id":     {c.ClientID}, +		"redirect_uri":  internal.CondVal(c.RedirectURL), +		"scope":         internal.CondVal(strings.Join(c.Scopes, " ")), +		"state":         internal.CondVal(state), +	} +	for _, opt := range opts { +		opt.setValue(v) +	} +	if strings.Contains(c.Endpoint.AuthURL, "?") { +		buf.WriteByte('&') +	} else { +		buf.WriteByte('?') +	} +	buf.WriteString(v.Encode()) +	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":      internal.CondVal(strings.Join(c.Scopes, " ")), +	}) +} + +// Exchange converts an authorization code into a token. +// +// It is used after a resource provider redirects the user back +// to the Redirect URI (the URL obtained from AuthCodeURL). +// +// 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 +// calling Exchange, be sure to validate FormValue("state"). +func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) { +	return retrieveToken(ctx, c, url.Values{ +		"grant_type":   {"authorization_code"}, +		"code":         {code}, +		"redirect_uri": internal.CondVal(c.RedirectURL), +		"scope":        internal.CondVal(strings.Join(c.Scopes, " ")), +	}) +} + +// Client returns an HTTP client using the provided token. +// The token will auto-refresh as necessary. The underlying +// HTTP transport will be obtained using the provided context. +// The returned client and its Transport should not be modified. +func (c *Config) Client(ctx context.Context, t *Token) *http.Client { +	return NewClient(ctx, c.TokenSource(ctx, t)) +} + +// TokenSource returns a TokenSource that returns t until t expires, +// automatically refreshing it as necessary using the provided context. +// +// Most users will use Config.Client instead. +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" +// HTTP requests to renew a token using a RefreshToken. +type tokenRefresher struct { +	ctx          context.Context // used to get HTTP requests +	conf         *Config +	refreshToken string +} + +// 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") +	} + +	tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{ +		"grant_type":    {"refresh_token"}, +		"refresh_token": {tf.refreshToken}, +	}) + +	if err != nil { +		return nil, err +	} +	if tf.refreshToken != tk.RefreshToken { +		tf.refreshToken = tk.RefreshToken +	} +	return tk, err +} + +// reuseTokenSource is a TokenSource that holds a single token in memory +// and validates its expiry before each call to retrieve it with +// Token. If it's expired, it will be auto-refreshed using the +// new TokenSource. +type reuseTokenSource struct { +	new TokenSource // called when t is expired. + +	mu sync.Mutex // guards t +	t  *Token +} + +// Token returns the current token if it's still valid, else will +// refresh the current token (using r.Context for HTTP client +// information) and return the new one. +func (s *reuseTokenSource) Token() (*Token, error) { +	s.mu.Lock() +	defer s.mu.Unlock() +	if s.t.Valid() { +		return s.t, nil +	} +	t, err := s.new.Token() +	if err != nil { +		return nil, err +	} +	s.t = t +	return t, nil +} + +// StaticTokenSource returns a TokenSource that always returns the same token. +// Because the provided token t is never refreshed, StaticTokenSource is only +// useful for tokens that never expire. +func StaticTokenSource(t *Token) TokenSource { +	return staticTokenSource{t} +} + +// staticTokenSource is a TokenSource that always returns the same Token. +type staticTokenSource struct { +	t *Token +} + +func (s staticTokenSource) Token() (*Token, error) { +	return s.t, nil +} + +// HTTPClient is the context key to use with golang.org/x/net/context's +// WithValue function to associate an *http.Client value with a context. +var HTTPClient internal.ContextKey + +// NewClient creates an *http.Client from a Context and TokenSource. +// 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 := internal.ContextClient(ctx) +		if err != nil { +			return &http.Client{Transport: internal.ErrorTransport{err}} +		} +		return c +	} +	return &http.Client{ +		Transport: &Transport{ +			Base:   internal.ContextTransport(ctx), +			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, +	} +} diff --git a/vendor/golang.org/x/oauth2/token.go b/vendor/golang.org/x/oauth2/token.go new file mode 100644 index 0000000..7a3167f --- /dev/null +++ b/vendor/golang.org/x/oauth2/token.go @@ -0,0 +1,158 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package oauth2 + +import ( +	"net/http" +	"net/url" +	"strconv" +	"strings" +	"time" + +	"golang.org/x/net/context" +	"golang.org/x/oauth2/internal" +) + +// 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 +// the requests to access protected resources on the OAuth 2.0 +// provider's backend. +// +// Most users of this package should not access fields of Token +// directly. They're exported mostly for use by related packages +// implementing derivative OAuth2 flows. +type Token struct { +	// AccessToken is the token that authorizes and authenticates +	// the requests. +	AccessToken string `json:"access_token"` + +	// TokenType is the type of token. +	// The Type method returns either this or "Bearer", the default. +	TokenType string `json:"token_type,omitempty"` + +	// RefreshToken is a token that's used by the application +	// (as opposed to the user) to refresh the access token +	// if it expires. +	RefreshToken string `json:"refresh_token,omitempty"` + +	// Expiry is the optional expiration time of the access token. +	// +	// If zero, TokenSource implementations will reuse the same +	// token forever and RefreshToken or equivalent +	// mechanisms for that TokenSource will not be used. +	Expiry time.Time `json:"expiry,omitempty"` + +	// raw optionally contains extra metadata from the server +	// when updating a token. +	raw interface{} +} + +// Type returns t.TokenType if non-empty, else "Bearer". +func (t *Token) Type() string { +	if strings.EqualFold(t.TokenType, "bearer") { +		return "Bearer" +	} +	if strings.EqualFold(t.TokenType, "mac") { +		return "MAC" +	} +	if strings.EqualFold(t.TokenType, "basic") { +		return "Basic" +	} +	if t.TokenType != "" { +		return t.TokenType +	} +	return "Bearer" +} + +// SetAuthHeader sets the Authorization header to r using the access +// token in t. +// +// This method is unnecessary when using Transport or an HTTP Client +// returned by this package. +func (t *Token) SetAuthHeader(r *http.Request) { +	r.Header.Set("Authorization", t.Type()+" "+t.AccessToken) +} + +// 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 raw, ok := t.raw.(map[string]interface{}); ok { +		return raw[key] +	} + +	vals, ok := t.raw.(url.Values) +	if !ok { +		return nil +	} + +	v := vals.Get(key) +	switch s := strings.TrimSpace(v); strings.Count(s, ".") { +	case 0: // Contains no "."; try to parse as int +		if i, err := strconv.ParseInt(s, 10, 64); err == nil { +			return i +		} +	case 1: // Contains a single "."; try to parse as float +		if f, err := strconv.ParseFloat(s, 64); err == nil { +			return f +		} +	} + +	return v +} + +// expired reports whether the token is expired. +// t must be non-nil. +func (t *Token) expired() bool { +	if t.Expiry.IsZero() { +		return false +	} +	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() +} + +// tokenFromInternal maps an *internal.Token struct into +// a *Token struct. +func tokenFromInternal(t *internal.Token) *Token { +	if t == nil { +		return nil +	} +	return &Token{ +		AccessToken:  t.AccessToken, +		TokenType:    t.TokenType, +		RefreshToken: t.RefreshToken, +		Expiry:       t.Expiry, +		raw:          t.Raw, +	} +} + +// retrieveToken takes a *Config and uses that to retrieve an *internal.Token. +// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along +// with an error.. +func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) { +	tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v) +	if err != nil { +		return nil, err +	} +	return tokenFromInternal(tk), nil +} diff --git a/vendor/golang.org/x/oauth2/transport.go b/vendor/golang.org/x/oauth2/transport.go new file mode 100644 index 0000000..92ac7e2 --- /dev/null +++ b/vendor/golang.org/x/oauth2/transport.go @@ -0,0 +1,132 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package oauth2 + +import ( +	"errors" +	"io" +	"net/http" +	"sync" +) + +// Transport is an http.RoundTripper that makes OAuth 2.0 HTTP requests, +// wrapping a base RoundTripper and adding an Authorization header +// with a token from the supplied Sources. +// +// Transport is a low-level mechanism. Most code will use the +// higher-level Config.Client method instead. +type Transport struct { +	// Source supplies the token to add to outgoing requests' +	// Authorization headers. +	Source TokenSource + +	// Base is the base RoundTripper used to make HTTP requests. +	// If nil, http.DefaultTransport is used. +	Base http.RoundTripper + +	mu     sync.Mutex                      // guards modReq +	modReq map[*http.Request]*http.Request // original -> modified +} + +// RoundTrip authorizes and authenticates the request with an +// access token. If no token exists or token is expired, +// tries to refresh/fetch a new token. +func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { +	if t.Source == nil { +		return nil, errors.New("oauth2: Transport's Source is nil") +	} +	token, err := t.Source.Token() +	if err != nil { +		return nil, err +	} + +	req2 := cloneRequest(req) // per RoundTripper contract +	token.SetAuthHeader(req2) +	t.setModReq(req, req2) +	res, err := t.base().RoundTrip(req2) +	if err != nil { +		t.setModReq(req, nil) +		return nil, err +	} +	res.Body = &onEOFReader{ +		rc: res.Body, +		fn: func() { t.setModReq(req, nil) }, +	} +	return res, nil +} + +// CancelRequest cancels an in-flight request by closing its connection. +func (t *Transport) CancelRequest(req *http.Request) { +	type canceler interface { +		CancelRequest(*http.Request) +	} +	if cr, ok := t.base().(canceler); ok { +		t.mu.Lock() +		modReq := t.modReq[req] +		delete(t.modReq, req) +		t.mu.Unlock() +		cr.CancelRequest(modReq) +	} +} + +func (t *Transport) base() http.RoundTripper { +	if t.Base != nil { +		return t.Base +	} +	return http.DefaultTransport +} + +func (t *Transport) setModReq(orig, mod *http.Request) { +	t.mu.Lock() +	defer t.mu.Unlock() +	if t.modReq == nil { +		t.modReq = make(map[*http.Request]*http.Request) +	} +	if mod == nil { +		delete(t.modReq, orig) +	} else { +		t.modReq[orig] = mod +	} +} + +// cloneRequest returns a clone of the provided *http.Request. +// The clone is a shallow copy of the struct and its Header map. +func cloneRequest(r *http.Request) *http.Request { +	// shallow copy of the struct +	r2 := new(http.Request) +	*r2 = *r +	// deep copy of the Header +	r2.Header = make(http.Header, len(r.Header)) +	for k, s := range r.Header { +		r2.Header[k] = append([]string(nil), s...) +	} +	return r2 +} + +type onEOFReader struct { +	rc io.ReadCloser +	fn func() +} + +func (r *onEOFReader) Read(p []byte) (n int, err error) { +	n, err = r.rc.Read(p) +	if err == io.EOF { +		r.runFunc() +	} +	return +} + +func (r *onEOFReader) Close() error { +	err := r.rc.Close() +	r.runFunc() +	return err +} + +func (r *onEOFReader) runFunc() { +	if fn := r.fn; fn != nil { +		fn() +		r.fn = nil +	} +}  | 
