Home | History | Annotate | Download | only in oauth2
      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 oauth2 provides support for making
      6 // OAuth2 authorized and authenticated HTTP requests.
      7 // It can additionally grant authorization with Bearer JWT.
      8 package oauth2
      9 
     10 import (
     11 	"bytes"
     12 	"errors"
     13 	"net/http"
     14 	"net/url"
     15 	"strings"
     16 	"sync"
     17 
     18 	"golang.org/x/net/context"
     19 	"golang.org/x/oauth2/internal"
     20 )
     21 
     22 // NoContext is the default context you should supply if not using
     23 // your own context.Context (see https://golang.org/x/net/context).
     24 //
     25 // Deprecated: Use context.Background() or context.TODO() instead.
     26 var NoContext = context.TODO()
     27 
     28 // RegisterBrokenAuthHeaderProvider registers an OAuth2 server
     29 // identified by the tokenURL prefix as an OAuth2 implementation
     30 // which doesn't support the HTTP Basic authentication
     31 // scheme to authenticate with the authorization server.
     32 // Once a server is registered, credentials (client_id and client_secret)
     33 // will be passed as query parameters rather than being present
     34 // in the Authorization header.
     35 // See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
     36 func RegisterBrokenAuthHeaderProvider(tokenURL string) {
     37 	internal.RegisterBrokenAuthHeaderProvider(tokenURL)
     38 }
     39 
     40 // Config describes a typical 3-legged OAuth2 flow, with both the
     41 // client application information and the server's endpoint URLs.
     42 // For the client credentials 2-legged OAuth2 flow, see the clientcredentials
     43 // package (https://golang.org/x/oauth2/clientcredentials).
     44 type Config struct {
     45 	// ClientID is the application's ID.
     46 	ClientID string
     47 
     48 	// ClientSecret is the application's secret.
     49 	ClientSecret string
     50 
     51 	// Endpoint contains the resource server's token endpoint
     52 	// URLs. These are constants specific to each server and are
     53 	// often available via site-specific packages, such as
     54 	// google.Endpoint or github.Endpoint.
     55 	Endpoint Endpoint
     56 
     57 	// RedirectURL is the URL to redirect users going through
     58 	// the OAuth flow, after the resource owner's URLs.
     59 	RedirectURL string
     60 
     61 	// Scope specifies optional requested permissions.
     62 	Scopes []string
     63 }
     64 
     65 // A TokenSource is anything that can return a token.
     66 type TokenSource interface {
     67 	// Token returns a token or an error.
     68 	// Token must be safe for concurrent use by multiple goroutines.
     69 	// The returned Token must not be modified.
     70 	Token() (*Token, error)
     71 }
     72 
     73 // Endpoint contains the OAuth 2.0 provider's authorization and token
     74 // endpoint URLs.
     75 type Endpoint struct {
     76 	AuthURL  string
     77 	TokenURL string
     78 }
     79 
     80 var (
     81 	// AccessTypeOnline and AccessTypeOffline are options passed
     82 	// to the Options.AuthCodeURL method. They modify the
     83 	// "access_type" field that gets sent in the URL returned by
     84 	// AuthCodeURL.
     85 	//
     86 	// Online is the default if neither is specified. If your
     87 	// application needs to refresh access tokens when the user
     88 	// is not present at the browser, then use offline. This will
     89 	// result in your application obtaining a refresh token the
     90 	// first time your application exchanges an authorization
     91 	// code for a user.
     92 	AccessTypeOnline  AuthCodeOption = SetAuthURLParam("access_type", "online")
     93 	AccessTypeOffline AuthCodeOption = SetAuthURLParam("access_type", "offline")
     94 
     95 	// ApprovalForce forces the users to view the consent dialog
     96 	// and confirm the permissions request at the URL returned
     97 	// from AuthCodeURL, even if they've already done so.
     98 	ApprovalForce AuthCodeOption = SetAuthURLParam("approval_prompt", "force")
     99 )
    100 
    101 // An AuthCodeOption is passed to Config.AuthCodeURL.
    102 type AuthCodeOption interface {
    103 	setValue(url.Values)
    104 }
    105 
    106 type setParam struct{ k, v string }
    107 
    108 func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) }
    109 
    110 // SetAuthURLParam builds an AuthCodeOption which passes key/value parameters
    111 // to a provider's authorization endpoint.
    112 func SetAuthURLParam(key, value string) AuthCodeOption {
    113 	return setParam{key, value}
    114 }
    115 
    116 // AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
    117 // that asks for permissions for the required scopes explicitly.
    118 //
    119 // State is a token to protect the user from CSRF attacks. You must
    120 // always provide a non-zero string and validate that it matches the
    121 // the state query parameter on your redirect callback.
    122 // See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
    123 //
    124 // Opts may include AccessTypeOnline or AccessTypeOffline, as well
    125 // as ApprovalForce.
    126 func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
    127 	var buf bytes.Buffer
    128 	buf.WriteString(c.Endpoint.AuthURL)
    129 	v := url.Values{
    130 		"response_type": {"code"},
    131 		"client_id":     {c.ClientID},
    132 		"redirect_uri":  internal.CondVal(c.RedirectURL),
    133 		"scope":         internal.CondVal(strings.Join(c.Scopes, " ")),
    134 		"state":         internal.CondVal(state),
    135 	}
    136 	for _, opt := range opts {
    137 		opt.setValue(v)
    138 	}
    139 	if strings.Contains(c.Endpoint.AuthURL, "?") {
    140 		buf.WriteByte('&')
    141 	} else {
    142 		buf.WriteByte('?')
    143 	}
    144 	buf.WriteString(v.Encode())
    145 	return buf.String()
    146 }
    147 
    148 // PasswordCredentialsToken converts a resource owner username and password
    149 // pair into a token.
    150 //
    151 // Per the RFC, this grant type should only be used "when there is a high
    152 // degree of trust between the resource owner and the client (e.g., the client
    153 // is part of the device operating system or a highly privileged application),
    154 // and when other authorization grant types are not available."
    155 // See https://tools.ietf.org/html/rfc6749#section-4.3 for more info.
    156 //
    157 // The HTTP client to use is derived from the context.
    158 // If nil, http.DefaultClient is used.
    159 func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) {
    160 	return retrieveToken(ctx, c, url.Values{
    161 		"grant_type": {"password"},
    162 		"username":   {username},
    163 		"password":   {password},
    164 		"scope":      internal.CondVal(strings.Join(c.Scopes, " ")),
    165 	})
    166 }
    167 
    168 // Exchange converts an authorization code into a token.
    169 //
    170 // It is used after a resource provider redirects the user back
    171 // to the Redirect URI (the URL obtained from AuthCodeURL).
    172 //
    173 // The HTTP client to use is derived from the context.
    174 // If a client is not provided via the context, http.DefaultClient is used.
    175 //
    176 // The code will be in the *http.Request.FormValue("code"). Before
    177 // calling Exchange, be sure to validate FormValue("state").
    178 func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) {
    179 	return retrieveToken(ctx, c, url.Values{
    180 		"grant_type":   {"authorization_code"},
    181 		"code":         {code},
    182 		"redirect_uri": internal.CondVal(c.RedirectURL),
    183 	})
    184 }
    185 
    186 // Client returns an HTTP client using the provided token.
    187 // The token will auto-refresh as necessary. The underlying
    188 // HTTP transport will be obtained using the provided context.
    189 // The returned client and its Transport should not be modified.
    190 func (c *Config) Client(ctx context.Context, t *Token) *http.Client {
    191 	return NewClient(ctx, c.TokenSource(ctx, t))
    192 }
    193 
    194 // TokenSource returns a TokenSource that returns t until t expires,
    195 // automatically refreshing it as necessary using the provided context.
    196 //
    197 // Most users will use Config.Client instead.
    198 func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource {
    199 	tkr := &tokenRefresher{
    200 		ctx:  ctx,
    201 		conf: c,
    202 	}
    203 	if t != nil {
    204 		tkr.refreshToken = t.RefreshToken
    205 	}
    206 	return &reuseTokenSource{
    207 		t:   t,
    208 		new: tkr,
    209 	}
    210 }
    211 
    212 // tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token"
    213 // HTTP requests to renew a token using a RefreshToken.
    214 type tokenRefresher struct {
    215 	ctx          context.Context // used to get HTTP requests
    216 	conf         *Config
    217 	refreshToken string
    218 }
    219 
    220 // WARNING: Token is not safe for concurrent access, as it
    221 // updates the tokenRefresher's refreshToken field.
    222 // Within this package, it is used by reuseTokenSource which
    223 // synchronizes calls to this method with its own mutex.
    224 func (tf *tokenRefresher) Token() (*Token, error) {
    225 	if tf.refreshToken == "" {
    226 		return nil, errors.New("oauth2: token expired and refresh token is not set")
    227 	}
    228 
    229 	tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{
    230 		"grant_type":    {"refresh_token"},
    231 		"refresh_token": {tf.refreshToken},
    232 	})
    233 
    234 	if err != nil {
    235 		return nil, err
    236 	}
    237 	if tf.refreshToken != tk.RefreshToken {
    238 		tf.refreshToken = tk.RefreshToken
    239 	}
    240 	return tk, err
    241 }
    242 
    243 // reuseTokenSource is a TokenSource that holds a single token in memory
    244 // and validates its expiry before each call to retrieve it with
    245 // Token. If it's expired, it will be auto-refreshed using the
    246 // new TokenSource.
    247 type reuseTokenSource struct {
    248 	new TokenSource // called when t is expired.
    249 
    250 	mu sync.Mutex // guards t
    251 	t  *Token
    252 }
    253 
    254 // Token returns the current token if it's still valid, else will
    255 // refresh the current token (using r.Context for HTTP client
    256 // information) and return the new one.
    257 func (s *reuseTokenSource) Token() (*Token, error) {
    258 	s.mu.Lock()
    259 	defer s.mu.Unlock()
    260 	if s.t.Valid() {
    261 		return s.t, nil
    262 	}
    263 	t, err := s.new.Token()
    264 	if err != nil {
    265 		return nil, err
    266 	}
    267 	s.t = t
    268 	return t, nil
    269 }
    270 
    271 // StaticTokenSource returns a TokenSource that always returns the same token.
    272 // Because the provided token t is never refreshed, StaticTokenSource is only
    273 // useful for tokens that never expire.
    274 func StaticTokenSource(t *Token) TokenSource {
    275 	return staticTokenSource{t}
    276 }
    277 
    278 // staticTokenSource is a TokenSource that always returns the same Token.
    279 type staticTokenSource struct {
    280 	t *Token
    281 }
    282 
    283 func (s staticTokenSource) Token() (*Token, error) {
    284 	return s.t, nil
    285 }
    286 
    287 // HTTPClient is the context key to use with golang.org/x/net/context's
    288 // WithValue function to associate an *http.Client value with a context.
    289 var HTTPClient internal.ContextKey
    290 
    291 // NewClient creates an *http.Client from a Context and TokenSource.
    292 // The returned client is not valid beyond the lifetime of the context.
    293 //
    294 // As a special case, if src is nil, a non-OAuth2 client is returned
    295 // using the provided context. This exists to support related OAuth2
    296 // packages.
    297 func NewClient(ctx context.Context, src TokenSource) *http.Client {
    298 	if src == nil {
    299 		c, err := internal.ContextClient(ctx)
    300 		if err != nil {
    301 			return &http.Client{Transport: internal.ErrorTransport{Err: err}}
    302 		}
    303 		return c
    304 	}
    305 	return &http.Client{
    306 		Transport: &Transport{
    307 			Base:   internal.ContextTransport(ctx),
    308 			Source: ReuseTokenSource(nil, src),
    309 		},
    310 	}
    311 }
    312 
    313 // ReuseTokenSource returns a TokenSource which repeatedly returns the
    314 // same token as long as it's valid, starting with t.
    315 // When its cached token is invalid, a new token is obtained from src.
    316 //
    317 // ReuseTokenSource is typically used to reuse tokens from a cache
    318 // (such as a file on disk) between runs of a program, rather than
    319 // obtaining new tokens unnecessarily.
    320 //
    321 // The initial token t may be nil, in which case the TokenSource is
    322 // wrapped in a caching version if it isn't one already. This also
    323 // means it's always safe to wrap ReuseTokenSource around any other
    324 // TokenSource without adverse effects.
    325 func ReuseTokenSource(t *Token, src TokenSource) TokenSource {
    326 	// Don't wrap a reuseTokenSource in itself. That would work,
    327 	// but cause an unnecessary number of mutex operations.
    328 	// Just build the equivalent one.
    329 	if rt, ok := src.(*reuseTokenSource); ok {
    330 		if t == nil {
    331 			// Just use it directly.
    332 			return rt
    333 		}
    334 		src = rt.new
    335 	}
    336 	return &reuseTokenSource{
    337 		t:   t,
    338 		new: src,
    339 	}
    340 }
    341