token.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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. package oauth2
  5. import (
  6. "fmt"
  7. "net/http"
  8. "net/url"
  9. "strconv"
  10. "strings"
  11. "time"
  12. "golang.org/x/net/context"
  13. "golang.org/x/oauth2/internal"
  14. )
  15. // expiryDelta determines how earlier a token should be considered
  16. // expired than its actual expiration time. It is used to avoid late
  17. // expirations due to client-server time mismatches.
  18. const expiryDelta = 10 * time.Second
  19. // Token represents the credentials used to authorize
  20. // the requests to access protected resources on the OAuth 2.0
  21. // provider's backend.
  22. //
  23. // Most users of this package should not access fields of Token
  24. // directly. They're exported mostly for use by related packages
  25. // implementing derivative OAuth2 flows.
  26. type Token struct {
  27. // AccessToken is the token that authorizes and authenticates
  28. // the requests.
  29. AccessToken string `json:"access_token"`
  30. // TokenType is the type of token.
  31. // The Type method returns either this or "Bearer", the default.
  32. TokenType string `json:"token_type,omitempty"`
  33. // RefreshToken is a token that's used by the application
  34. // (as opposed to the user) to refresh the access token
  35. // if it expires.
  36. RefreshToken string `json:"refresh_token,omitempty"`
  37. // Expiry is the optional expiration time of the access token.
  38. //
  39. // If zero, TokenSource implementations will reuse the same
  40. // token forever and RefreshToken or equivalent
  41. // mechanisms for that TokenSource will not be used.
  42. Expiry time.Time `json:"expiry,omitempty"`
  43. // raw optionally contains extra metadata from the server
  44. // when updating a token.
  45. raw interface{}
  46. }
  47. // Type returns t.TokenType if non-empty, else "Bearer".
  48. func (t *Token) Type() string {
  49. if strings.EqualFold(t.TokenType, "bearer") {
  50. return "Bearer"
  51. }
  52. if strings.EqualFold(t.TokenType, "mac") {
  53. return "MAC"
  54. }
  55. if strings.EqualFold(t.TokenType, "basic") {
  56. return "Basic"
  57. }
  58. if t.TokenType != "" {
  59. return t.TokenType
  60. }
  61. return "Bearer"
  62. }
  63. // SetAuthHeader sets the Authorization header to r using the access
  64. // token in t.
  65. //
  66. // This method is unnecessary when using Transport or an HTTP Client
  67. // returned by this package.
  68. func (t *Token) SetAuthHeader(r *http.Request) {
  69. r.Header.Set("Authorization", t.Type()+" "+t.AccessToken)
  70. }
  71. // WithExtra returns a new Token that's a clone of t, but using the
  72. // provided raw extra map. This is only intended for use by packages
  73. // implementing derivative OAuth2 flows.
  74. func (t *Token) WithExtra(extra interface{}) *Token {
  75. t2 := new(Token)
  76. *t2 = *t
  77. t2.raw = extra
  78. return t2
  79. }
  80. // Extra returns an extra field.
  81. // Extra fields are key-value pairs returned by the server as a
  82. // part of the token retrieval response.
  83. func (t *Token) Extra(key string) interface{} {
  84. if raw, ok := t.raw.(map[string]interface{}); ok {
  85. return raw[key]
  86. }
  87. vals, ok := t.raw.(url.Values)
  88. if !ok {
  89. return nil
  90. }
  91. v := vals.Get(key)
  92. switch s := strings.TrimSpace(v); strings.Count(s, ".") {
  93. case 0: // Contains no "."; try to parse as int
  94. if i, err := strconv.ParseInt(s, 10, 64); err == nil {
  95. return i
  96. }
  97. case 1: // Contains a single "."; try to parse as float
  98. if f, err := strconv.ParseFloat(s, 64); err == nil {
  99. return f
  100. }
  101. }
  102. return v
  103. }
  104. // expired reports whether the token is expired.
  105. // t must be non-nil.
  106. func (t *Token) expired() bool {
  107. if t.Expiry.IsZero() {
  108. return false
  109. }
  110. return t.Expiry.Round(0).Add(-expiryDelta).Before(time.Now())
  111. }
  112. // Valid reports whether t is non-nil, has an AccessToken, and is not expired.
  113. func (t *Token) Valid() bool {
  114. return t != nil && t.AccessToken != "" && !t.expired()
  115. }
  116. // tokenFromInternal maps an *internal.Token struct into
  117. // a *Token struct.
  118. func tokenFromInternal(t *internal.Token) *Token {
  119. if t == nil {
  120. return nil
  121. }
  122. return &Token{
  123. AccessToken: t.AccessToken,
  124. TokenType: t.TokenType,
  125. RefreshToken: t.RefreshToken,
  126. Expiry: t.Expiry,
  127. raw: t.Raw,
  128. }
  129. }
  130. // retrieveToken takes a *Config and uses that to retrieve an *internal.Token.
  131. // This token is then mapped from *internal.Token into an *oauth2.Token which is returned along
  132. // with an error..
  133. func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
  134. tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v)
  135. if err != nil {
  136. if rErr, ok := err.(*internal.RetrieveError); ok {
  137. return nil, (*RetrieveError)(rErr)
  138. }
  139. return nil, err
  140. }
  141. return tokenFromInternal(tk), nil
  142. }
  143. // RetrieveError is the error returned when the token endpoint returns a
  144. // non-2XX HTTP status code.
  145. type RetrieveError struct {
  146. Response *http.Response
  147. // Body is the body that was consumed by reading Response.Body.
  148. // It may be truncated.
  149. Body []byte
  150. }
  151. func (r *RetrieveError) Error() string {
  152. return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
  153. }