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

import (
	
	
	
	
	
	
	
	
	
	
	
	
	
	

	
)
Token represents the credentials 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.
AccessToken is the token that authorizes and authenticates the requests.
TokenType is the type of token. The Type method returns either this or "Bearer", the default.
RefreshToken is a token that's used by the application (as opposed to the user) to refresh the access token if it expires.
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.
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
}

func ( *tokenJSON) () ( time.Time) {
	if  := .ExpiresIn;  != 0 {
		return time.Now().Add(time.Duration() * time.Second)
	}
	return
}

type expirationTime int32

func ( *expirationTime) ( []byte) error {
	if len() == 0 || string() == "null" {
		return nil
	}
	var  json.Number
	 := json.Unmarshal(, &)
	if  != nil {
		return 
	}
	,  := .Int64()
	if  != nil {
		return 
	}
	if  > math.MaxInt32 {
		 = math.MaxInt32
	}
	* = expirationTime()
	return nil
}
RegisterBrokenAuthHeaderProvider previously did something. It is now a no-op. Deprecated: this function no longer does anything. Caller code that wants to avoid potential extra HTTP requests made during auto-probing of the provider's auth style should set Endpoint.AuthStyle.
AuthStyle is a copy of the golang.org/x/oauth2 package's AuthStyle type.
authStyleCache is the set of tokenURLs we've successfully used via RetrieveToken and which style auth we ended up using. It's called a cache, but it doesn't (yet?) shrink. It's expected that the set of OAuth2 servers a program contacts over time is fixed and small.
var authStyleCache struct {
	sync.Mutex
	m map[string]AuthStyle // keyed by tokenURL
}
ResetAuthCache resets the global authentication style cache used for AuthStyleUnknown token requests.
lookupAuthStyle reports which auth style we last used with tokenURL when calling RetrieveToken and whether we have ever done so.
func ( string) ( AuthStyle,  bool) {
	authStyleCache.Lock()
	defer authStyleCache.Unlock()
	,  = authStyleCache.m[]
	return
}
setAuthStyle adds an entry to authStyleCache, documented above.
newTokenRequest returns a new *http.Request to retrieve a new token from tokenURL using the provided clientID, clientSecret, and POST body parameters. inParams is whether the clientID & clientSecret should be encoded as the POST body. An 'inParams' value of true means to send it in the POST body (along with any values in v); false means to send it in the Authorization header.
func (, ,  string,  url.Values,  AuthStyle) (*http.Request, error) {
	if  == AuthStyleInParams {
		 = cloneURLValues()
		if  != "" {
			.Set("client_id", )
		}
		if  != "" {
			.Set("client_secret", )
		}
	}
	,  := http.NewRequest("POST", , strings.NewReader(.Encode()))
	if  != nil {
		return nil, 
	}
	.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if  == AuthStyleInHeader {
		.SetBasicAuth(url.QueryEscape(), url.QueryEscape())
	}
	return , nil
}

func ( url.Values) url.Values {
	 := make(url.Values, len())
	for ,  := range  {
		[] = append([]string(nil), ...)
	}
	return 
}

func ( context.Context, , ,  string,  url.Values,  AuthStyle) (*Token, error) {
	 :=  == 0
	if  {
		if ,  := lookupAuthStyle();  {
			 = 
			 = false
		} else {
			 = AuthStyleInHeader // the first way we'll try
		}
	}
	,  := newTokenRequest(, , , , )
	if  != nil {
		return nil, 
	}
	,  := doTokenRoundTrip(, )
If we get an error, assume the server wants the clientID & clientSecret in a different form. 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 We used to maintain a big table in this code of all the sites and which way they went, but maintaining it didn't scale & got annoying. So just try both ways.
		 = AuthStyleInParams // the second way we'll try
		, _ = newTokenRequest(, , , , )
		,  = doTokenRoundTrip(, )
	}
	if  &&  == nil {
		setAuthStyle(, )
Don't overwrite `RefreshToken` with an empty value if this was a token refreshing request.
	if  != nil && .RefreshToken == "" {
		.RefreshToken = .Get("refresh_token")
	}
	return , 
}

func ( context.Context,  *http.Request) (*Token, error) {
	,  := ctxhttp.Do(, ContextClient(), )
	if  != nil {
		return nil, 
	}
	,  := ioutil.ReadAll(io.LimitReader(.Body, 1<<20))
	.Body.Close()
	if  != nil {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", )
	}
	if  := .StatusCode;  < 200 ||  > 299 {
		return nil, &RetrieveError{
			Response: ,
			Body:     ,
		}
	}

	var  *Token
	, ,  := mime.ParseMediaType(.Header.Get("Content-Type"))
	switch  {
	case "application/x-www-form-urlencoded", "text/plain":
		,  := url.ParseQuery(string())
		if  != nil {
			return nil, 
		}
		 = &Token{
			AccessToken:  .Get("access_token"),
			TokenType:    .Get("token_type"),
			RefreshToken: .Get("refresh_token"),
			Raw:          ,
		}
		 := .Get("expires_in")
		,  := strconv.Atoi()
		if  != 0 {
			.Expiry = time.Now().Add(time.Duration() * time.Second)
		}
	default:
		var  tokenJSON
		if  = json.Unmarshal(, &);  != nil {
			return nil, 
		}
		 = &Token{
			AccessToken:  .AccessToken,
			TokenType:    .TokenType,
			RefreshToken: .RefreshToken,
			Expiry:       .expiry(),
			Raw:          make(map[string]interface{}),
		}
		json.Unmarshal(, &.Raw) // no error checks for optional fields
	}
	if .AccessToken == "" {
		return nil, errors.New("oauth2: server response missing access_token")
	}
	return , nil
}

type RetrieveError struct {
	Response *http.Response
	Body     []byte
}

func ( *RetrieveError) () string {
	return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", .Response.Status, .Body)