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. 8package oauth2 9 10import ( 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. 26var 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. 36func 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). 44type 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. 66type 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. 75type Endpoint struct { 76 AuthURL string 77 TokenURL string 78} 79 80var ( 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. 102type AuthCodeOption interface { 103 setValue(url.Values) 104} 105 106type setParam struct{ k, v string } 107 108func (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. 112func 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. 126func (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. 159func (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"). 178func (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. 190func (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. 198func (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. 214type 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. 224func (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. 247type 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. 257func (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. 274func StaticTokenSource(t *Token) TokenSource { 275 return staticTokenSource{t} 276} 277 278// staticTokenSource is a TokenSource that always returns the same Token. 279type staticTokenSource struct { 280 t *Token 281} 282 283func (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. 289var 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. 297func 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. 325func 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