...

Source file src/golang.org/x/oauth2/token.go

Documentation: golang.org/x/oauth2

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package oauth2
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"net/http"
    11  	"net/url"
    12  	"strconv"
    13  	"strings"
    14  	"time"
    15  
    16  	"golang.org/x/oauth2/internal"
    17  )
    18  
    19  // defaultExpiryDelta determines how earlier a token should be considered
    20  // expired than its actual expiration time. It is used to avoid late
    21  // expirations due to client-server time mismatches.
    22  const defaultExpiryDelta = 10 * time.Second
    23  
    24  // Token represents the credentials used to authorize
    25  // the requests to access protected resources on the OAuth 2.0
    26  // provider's backend.
    27  //
    28  // Most users of this package should not access fields of Token
    29  // directly. They're exported mostly for use by related packages
    30  // implementing derivative OAuth2 flows.
    31  type Token struct {
    32  	// AccessToken is the token that authorizes and authenticates
    33  	// the requests.
    34  	AccessToken string `json:"access_token"`
    35  
    36  	// TokenType is the type of token.
    37  	// The Type method returns either this or "Bearer", the default.
    38  	TokenType string `json:"token_type,omitempty"`
    39  
    40  	// RefreshToken is a token that's used by the application
    41  	// (as opposed to the user) to refresh the access token
    42  	// if it expires.
    43  	RefreshToken string `json:"refresh_token,omitempty"`
    44  
    45  	// Expiry is the optional expiration time of the access token.
    46  	//
    47  	// If zero, TokenSource implementations will reuse the same
    48  	// token forever and RefreshToken or equivalent
    49  	// mechanisms for that TokenSource will not be used.
    50  	Expiry time.Time `json:"expiry,omitempty"`
    51  
    52  	// raw optionally contains extra metadata from the server
    53  	// when updating a token.
    54  	raw interface{}
    55  
    56  	// expiryDelta is used to calculate when a token is considered
    57  	// expired, by subtracting from Expiry. If zero, defaultExpiryDelta
    58  	// is used.
    59  	expiryDelta time.Duration
    60  }
    61  
    62  // Type returns t.TokenType if non-empty, else "Bearer".
    63  func (t *Token) Type() string {
    64  	if strings.EqualFold(t.TokenType, "bearer") {
    65  		return "Bearer"
    66  	}
    67  	if strings.EqualFold(t.TokenType, "mac") {
    68  		return "MAC"
    69  	}
    70  	if strings.EqualFold(t.TokenType, "basic") {
    71  		return "Basic"
    72  	}
    73  	if t.TokenType != "" {
    74  		return t.TokenType
    75  	}
    76  	return "Bearer"
    77  }
    78  
    79  // SetAuthHeader sets the Authorization header to r using the access
    80  // token in t.
    81  //
    82  // This method is unnecessary when using Transport or an HTTP Client
    83  // returned by this package.
    84  func (t *Token) SetAuthHeader(r *http.Request) {
    85  	r.Header.Set("Authorization", t.Type()+" "+t.AccessToken)
    86  }
    87  
    88  // WithExtra returns a new Token that's a clone of t, but using the
    89  // provided raw extra map. This is only intended for use by packages
    90  // implementing derivative OAuth2 flows.
    91  func (t *Token) WithExtra(extra interface{}) *Token {
    92  	t2 := new(Token)
    93  	*t2 = *t
    94  	t2.raw = extra
    95  	return t2
    96  }
    97  
    98  // Extra returns an extra field.
    99  // Extra fields are key-value pairs returned by the server as a
   100  // part of the token retrieval response.
   101  func (t *Token) Extra(key string) interface{} {
   102  	if raw, ok := t.raw.(map[string]interface{}); ok {
   103  		return raw[key]
   104  	}
   105  
   106  	vals, ok := t.raw.(url.Values)
   107  	if !ok {
   108  		return nil
   109  	}
   110  
   111  	v := vals.Get(key)
   112  	switch s := strings.TrimSpace(v); strings.Count(s, ".") {
   113  	case 0: // Contains no "."; try to parse as int
   114  		if i, err := strconv.ParseInt(s, 10, 64); err == nil {
   115  			return i
   116  		}
   117  	case 1: // Contains a single "."; try to parse as float
   118  		if f, err := strconv.ParseFloat(s, 64); err == nil {
   119  			return f
   120  		}
   121  	}
   122  
   123  	return v
   124  }
   125  
   126  // timeNow is time.Now but pulled out as a variable for tests.
   127  var timeNow = time.Now
   128  
   129  // expired reports whether the token is expired.
   130  // t must be non-nil.
   131  func (t *Token) expired() bool {
   132  	if t.Expiry.IsZero() {
   133  		return false
   134  	}
   135  
   136  	expiryDelta := defaultExpiryDelta
   137  	if t.expiryDelta != 0 {
   138  		expiryDelta = t.expiryDelta
   139  	}
   140  	return t.Expiry.Round(0).Add(-expiryDelta).Before(timeNow())
   141  }
   142  
   143  // Valid reports whether t is non-nil, has an AccessToken, and is not expired.
   144  func (t *Token) Valid() bool {
   145  	return t != nil && t.AccessToken != "" && !t.expired()
   146  }
   147  
   148  // tokenFromInternal maps an *internal.Token struct into
   149  // a *Token struct.
   150  func tokenFromInternal(t *internal.Token) *Token {
   151  	if t == nil {
   152  		return nil
   153  	}
   154  	return &Token{
   155  		AccessToken:  t.AccessToken,
   156  		TokenType:    t.TokenType,
   157  		RefreshToken: t.RefreshToken,
   158  		Expiry:       t.Expiry,
   159  		raw:          t.Raw,
   160  	}
   161  }
   162  
   163  // retrieveToken takes a *Config and uses that to retrieve an *internal.Token.
   164  // This token is then mapped from *internal.Token into an *oauth2.Token which is returned along
   165  // with an error..
   166  func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
   167  	tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v, internal.AuthStyle(c.Endpoint.AuthStyle), c.authStyleCache.Get())
   168  	if err != nil {
   169  		if rErr, ok := err.(*internal.RetrieveError); ok {
   170  			return nil, (*RetrieveError)(rErr)
   171  		}
   172  		return nil, err
   173  	}
   174  	return tokenFromInternal(tk), nil
   175  }
   176  
   177  // RetrieveError is the error returned when the token endpoint returns a
   178  // non-2XX HTTP status code or populates RFC 6749's 'error' parameter.
   179  // https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
   180  type RetrieveError struct {
   181  	Response *http.Response
   182  	// Body is the body that was consumed by reading Response.Body.
   183  	// It may be truncated.
   184  	Body []byte
   185  	// ErrorCode is RFC 6749's 'error' parameter.
   186  	ErrorCode string
   187  	// ErrorDescription is RFC 6749's 'error_description' parameter.
   188  	ErrorDescription string
   189  	// ErrorURI is RFC 6749's 'error_uri' parameter.
   190  	ErrorURI string
   191  }
   192  
   193  func (r *RetrieveError) Error() string {
   194  	if r.ErrorCode != "" {
   195  		s := fmt.Sprintf("oauth2: %q", r.ErrorCode)
   196  		if r.ErrorDescription != "" {
   197  			s += fmt.Sprintf(" %q", r.ErrorDescription)
   198  		}
   199  		if r.ErrorURI != "" {
   200  			s += fmt.Sprintf(" %q", r.ErrorURI)
   201  		}
   202  		return s
   203  	}
   204  	return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
   205  }
   206  

View as plain text