You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

179 lines
5.0 KiB

  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. "context"
  7. "fmt"
  8. "net/http"
  9. "net/url"
  10. "strconv"
  11. "strings"
  12. "time"
  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. // timeNow is time.Now but pulled out as a variable for tests.
  105. var timeNow = time.Now
  106. // expired reports whether the token is expired.
  107. // t must be non-nil.
  108. func (t *Token) expired() bool {
  109. if t.Expiry.IsZero() {
  110. return false
  111. }
  112. return t.Expiry.Round(0).Add(-expiryDelta).Before(timeNow())
  113. }
  114. // Valid reports whether t is non-nil, has an AccessToken, and is not expired.
  115. func (t *Token) Valid() bool {
  116. return t != nil && t.AccessToken != "" && !t.expired()
  117. }
  118. // tokenFromInternal maps an *internal.Token struct into
  119. // a *Token struct.
  120. func tokenFromInternal(t *internal.Token) *Token {
  121. if t == nil {
  122. return nil
  123. }
  124. return &Token{
  125. AccessToken: t.AccessToken,
  126. TokenType: t.TokenType,
  127. RefreshToken: t.RefreshToken,
  128. Expiry: t.Expiry,
  129. raw: t.Raw,
  130. }
  131. }
  132. // retrieveToken takes a *Config and uses that to retrieve an *internal.Token.
  133. // This token is then mapped from *internal.Token into an *oauth2.Token which is returned along
  134. // with an error..
  135. func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
  136. tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v, internal.AuthStyle(c.Endpoint.AuthStyle))
  137. if err != nil {
  138. if rErr, ok := err.(*internal.RetrieveError); ok {
  139. return nil, (*RetrieveError)(rErr)
  140. }
  141. return nil, err
  142. }
  143. return tokenFromInternal(tk), nil
  144. }
  145. // RetrieveError is the error returned when the token endpoint returns a
  146. // non-2XX HTTP status code.
  147. type RetrieveError struct {
  148. Response *http.Response
  149. // Body is the body that was consumed by reading Response.Body.
  150. // It may be truncated.
  151. Body []byte
  152. }
  153. func (r *RetrieveError) Error() string {
  154. return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
  155. }