• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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