"Fossies" - the Fresh Open Source Software Archive

Member "gdrive-2.1.1/vendor/golang.org/x/oauth2/internal/token.go" (28 May 2021, 6830 Bytes) of package /linux/misc/gdrive-2.1.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    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 internal contains support packages for oauth2 package.
    6 package internal
    7 
    8 import (
    9     "encoding/json"
   10     "fmt"
   11     "io"
   12     "io/ioutil"
   13     "mime"
   14     "net/http"
   15     "net/url"
   16     "strconv"
   17     "strings"
   18     "time"
   19 
   20     "golang.org/x/net/context"
   21 )
   22 
   23 // Token represents the crendentials used to authorize
   24 // the requests to access protected resources on the OAuth 2.0
   25 // provider's backend.
   26 //
   27 // This type is a mirror of oauth2.Token and exists to break
   28 // an otherwise-circular dependency. Other internal packages
   29 // should convert this Token into an oauth2.Token before use.
   30 type Token struct {
   31     // AccessToken is the token that authorizes and authenticates
   32     // the requests.
   33     AccessToken string
   34 
   35     // TokenType is the type of token.
   36     // The Type method returns either this or "Bearer", the default.
   37     TokenType string
   38 
   39     // RefreshToken is a token that's used by the application
   40     // (as opposed to the user) to refresh the access token
   41     // if it expires.
   42     RefreshToken string
   43 
   44     // Expiry is the optional expiration time of the access token.
   45     //
   46     // If zero, TokenSource implementations will reuse the same
   47     // token forever and RefreshToken or equivalent
   48     // mechanisms for that TokenSource will not be used.
   49     Expiry time.Time
   50 
   51     // Raw optionally contains extra metadata from the server
   52     // when updating a token.
   53     Raw interface{}
   54 }
   55 
   56 // tokenJSON is the struct representing the HTTP response from OAuth2
   57 // providers returning a token in JSON form.
   58 type tokenJSON struct {
   59     AccessToken  string         `json:"access_token"`
   60     TokenType    string         `json:"token_type"`
   61     RefreshToken string         `json:"refresh_token"`
   62     ExpiresIn    expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
   63     Expires      expirationTime `json:"expires"`    // broken Facebook spelling of expires_in
   64 }
   65 
   66 func (e *tokenJSON) expiry() (t time.Time) {
   67     if v := e.ExpiresIn; v != 0 {
   68         return time.Now().Add(time.Duration(v) * time.Second)
   69     }
   70     if v := e.Expires; v != 0 {
   71         return time.Now().Add(time.Duration(v) * time.Second)
   72     }
   73     return
   74 }
   75 
   76 type expirationTime int32
   77 
   78 func (e *expirationTime) UnmarshalJSON(b []byte) error {
   79     var n json.Number
   80     err := json.Unmarshal(b, &n)
   81     if err != nil {
   82         return err
   83     }
   84     i, err := n.Int64()
   85     if err != nil {
   86         return err
   87     }
   88     *e = expirationTime(i)
   89     return nil
   90 }
   91 
   92 var brokenAuthHeaderProviders = []string{
   93     "https://accounts.google.com/",
   94     "https://api.dropbox.com/",
   95     "https://api.dropboxapi.com/",
   96     "https://api.instagram.com/",
   97     "https://api.netatmo.net/",
   98     "https://api.odnoklassniki.ru/",
   99     "https://api.pushbullet.com/",
  100     "https://api.soundcloud.com/",
  101     "https://api.twitch.tv/",
  102     "https://app.box.com/",
  103     "https://connect.stripe.com/",
  104     "https://login.microsoftonline.com/",
  105     "https://login.salesforce.com/",
  106     "https://oauth.sandbox.trainingpeaks.com/",
  107     "https://oauth.trainingpeaks.com/",
  108     "https://oauth.vk.com/",
  109     "https://openapi.baidu.com/",
  110     "https://slack.com/",
  111     "https://test-sandbox.auth.corp.google.com",
  112     "https://test.salesforce.com/",
  113     "https://user.gini.net/",
  114     "https://www.douban.com/",
  115     "https://www.googleapis.com/",
  116     "https://www.linkedin.com/",
  117     "https://www.strava.com/oauth/",
  118     "https://www.wunderlist.com/oauth/",
  119     "https://api.patreon.com/",
  120 }
  121 
  122 func RegisterBrokenAuthHeaderProvider(tokenURL string) {
  123     brokenAuthHeaderProviders = append(brokenAuthHeaderProviders, tokenURL)
  124 }
  125 
  126 // providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
  127 // implements the OAuth2 spec correctly
  128 // See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
  129 // In summary:
  130 // - Reddit only accepts client secret in the Authorization header
  131 // - Dropbox accepts either it in URL param or Auth header, but not both.
  132 // - Google only accepts URL param (not spec compliant?), not Auth header
  133 // - Stripe only accepts client secret in Auth header with Bearer method, not Basic
  134 func providerAuthHeaderWorks(tokenURL string) bool {
  135     for _, s := range brokenAuthHeaderProviders {
  136         if strings.HasPrefix(tokenURL, s) {
  137             // Some sites fail to implement the OAuth2 spec fully.
  138             return false
  139         }
  140     }
  141 
  142     // Assume the provider implements the spec properly
  143     // otherwise. We can add more exceptions as they're
  144     // discovered. We will _not_ be adding configurable hooks
  145     // to this package to let users select server bugs.
  146     return true
  147 }
  148 
  149 func RetrieveToken(ctx context.Context, ClientID, ClientSecret, TokenURL string, v url.Values) (*Token, error) {
  150     hc, err := ContextClient(ctx)
  151     if err != nil {
  152         return nil, err
  153     }
  154     v.Set("client_id", ClientID)
  155     bustedAuth := !providerAuthHeaderWorks(TokenURL)
  156     if bustedAuth && ClientSecret != "" {
  157         v.Set("client_secret", ClientSecret)
  158     }
  159     req, err := http.NewRequest("POST", TokenURL, strings.NewReader(v.Encode()))
  160     if err != nil {
  161         return nil, err
  162     }
  163     req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  164     if !bustedAuth {
  165         req.SetBasicAuth(ClientID, ClientSecret)
  166     }
  167     r, err := hc.Do(req)
  168     if err != nil {
  169         return nil, err
  170     }
  171     defer r.Body.Close()
  172     body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
  173     if err != nil {
  174         return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
  175     }
  176     if code := r.StatusCode; code < 200 || code > 299 {
  177         return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
  178     }
  179 
  180     var token *Token
  181     content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
  182     switch content {
  183     case "application/x-www-form-urlencoded", "text/plain":
  184         vals, err := url.ParseQuery(string(body))
  185         if err != nil {
  186             return nil, err
  187         }
  188         token = &Token{
  189             AccessToken:  vals.Get("access_token"),
  190             TokenType:    vals.Get("token_type"),
  191             RefreshToken: vals.Get("refresh_token"),
  192             Raw:          vals,
  193         }
  194         e := vals.Get("expires_in")
  195         if e == "" {
  196             // TODO(jbd): Facebook's OAuth2 implementation is broken and
  197             // returns expires_in field in expires. Remove the fallback to expires,
  198             // when Facebook fixes their implementation.
  199             e = vals.Get("expires")
  200         }
  201         expires, _ := strconv.Atoi(e)
  202         if expires != 0 {
  203             token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
  204         }
  205     default:
  206         var tj tokenJSON
  207         if err = json.Unmarshal(body, &tj); err != nil {
  208             return nil, err
  209         }
  210         token = &Token{
  211             AccessToken:  tj.AccessToken,
  212             TokenType:    tj.TokenType,
  213             RefreshToken: tj.RefreshToken,
  214             Expiry:       tj.expiry(),
  215             Raw:          make(map[string]interface{}),
  216         }
  217         json.Unmarshal(body, &token.Raw) // no error checks for optional fields
  218     }
  219     // Don't overwrite `RefreshToken` with an empty value
  220     // if this was a token refreshing request.
  221     if token.RefreshToken == "" {
  222         token.RefreshToken = v.Get("refresh_token")
  223     }
  224     return token, nil
  225 }