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