diff options
| author | Paul Zabelin | 2016-04-17 03:22:31 -0700 | 
|---|---|---|
| committer | Paul Zabelin | 2016-04-17 03:22:31 -0700 | 
| commit | b5eb2866cfceb69b0d4dd4948273d679a884fbb2 (patch) | |
| tree | 1fdb61a7138642a1612bb201434e8ebda141cc8a /vendor/golang.org/x/oauth2/internal/token.go | |
| parent | 8de8e05c483c6b5f23b14742315f1860211dcef7 (diff) | |
| download | gdrive-b5eb2866cfceb69b0d4dd4948273d679a884fbb2.tar.bz2 | |
add Go dependencies by godep
see https://github.com/tools/godep
Diffstat (limited to 'vendor/golang.org/x/oauth2/internal/token.go')
| -rw-r--r-- | vendor/golang.org/x/oauth2/internal/token.go | 225 | 
1 files changed, 225 insertions, 0 deletions
| 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 +} | 
