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 // import "golang.org/x/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-empty 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 } 133 if c.RedirectURL != "" { 134 v.Set("redirect_uri", c.RedirectURL) 135 } 136 if len(c.Scopes) > 0 { 137 v.Set("scope", strings.Join(c.Scopes, " ")) 138 } 139 if state != "" { 140 // TODO(light): Docs say never to omit state; don't allow empty. 141 v.Set("state", state) 142 } 143 for _, opt := range opts { 144 opt.setValue(v) 145 } 146 if strings.Contains(c.Endpoint.AuthURL, "?") { 147 buf.WriteByte('&') 148 } else { 149 buf.WriteByte('?') 150 } 151 buf.WriteString(v.Encode()) 152 return buf.String() 153} 154 155// PasswordCredentialsToken converts a resource owner username and password 156// pair into a token. 157// 158// Per the RFC, this grant type should only be used "when there is a high 159// degree of trust between the resource owner and the client (e.g., the client 160// is part of the device operating system or a highly privileged application), 161// and when other authorization grant types are not available." 162// See https://tools.ietf.org/html/rfc6749#section-4.3 for more info. 163// 164// The HTTP client to use is derived from the context. 165// If nil, http.DefaultClient is used. 166func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) { 167 v := url.Values{ 168 "grant_type": {"password"}, 169 "username": {username}, 170 "password": {password}, 171 } 172 if len(c.Scopes) > 0 { 173 v.Set("scope", strings.Join(c.Scopes, " ")) 174 } 175 return retrieveToken(ctx, c, v) 176} 177 178// Exchange converts an authorization code into a token. 179// 180// It is used after a resource provider redirects the user back 181// to the Redirect URI (the URL obtained from AuthCodeURL). 182// 183// The HTTP client to use is derived from the context. 184// If a client is not provided via the context, http.DefaultClient is used. 185// 186// The code will be in the *http.Request.FormValue("code"). Before 187// calling Exchange, be sure to validate FormValue("state"). 188func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) { 189 v := url.Values{ 190 "grant_type": {"authorization_code"}, 191 "code": {code}, 192 } 193 if c.RedirectURL != "" { 194 v.Set("redirect_uri", c.RedirectURL) 195 } 196 return retrieveToken(ctx, c, v) 197} 198 199// Client returns an HTTP client using the provided token. 200// The token will auto-refresh as necessary. The underlying 201// HTTP transport will be obtained using the provided context. 202// The returned client and its Transport should not be modified. 203func (c *Config) Client(ctx context.Context, t *Token) *http.Client { 204 return NewClient(ctx, c.TokenSource(ctx, t)) 205} 206 207// TokenSource returns a TokenSource that returns t until t expires, 208// automatically refreshing it as necessary using the provided context. 209// 210// Most users will use Config.Client instead. 211func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource { 212 tkr := &tokenRefresher{ 213 ctx: ctx, 214 conf: c, 215 } 216 if t != nil { 217 tkr.refreshToken = t.RefreshToken 218 } 219 return &reuseTokenSource{ 220 t: t, 221 new: tkr, 222 } 223} 224 225// tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token" 226// HTTP requests to renew a token using a RefreshToken. 227type tokenRefresher struct { 228 ctx context.Context // used to get HTTP requests 229 conf *Config 230 refreshToken string 231} 232 233// WARNING: Token is not safe for concurrent access, as it 234// updates the tokenRefresher's refreshToken field. 235// Within this package, it is used by reuseTokenSource which 236// synchronizes calls to this method with its own mutex. 237func (tf *tokenRefresher) Token() (*Token, error) { 238 if tf.refreshToken == "" { 239 return nil, errors.New("oauth2: token expired and refresh token is not set") 240 } 241 242 tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{ 243 "grant_type": {"refresh_token"}, 244 "refresh_token": {tf.refreshToken}, 245 }) 246 247 if err != nil { 248 return nil, err 249 } 250 if tf.refreshToken != tk.RefreshToken { 251 tf.refreshToken = tk.RefreshToken 252 } 253 return tk, err 254} 255 256// reuseTokenSource is a TokenSource that holds a single token in memory 257// and validates its expiry before each call to retrieve it with 258// Token. If it's expired, it will be auto-refreshed using the 259// new TokenSource. 260type reuseTokenSource struct { 261 new TokenSource // called when t is expired. 262 263 mu sync.Mutex // guards t 264 t *Token 265} 266 267// Token returns the current token if it's still valid, else will 268// refresh the current token (using r.Context for HTTP client 269// information) and return the new one. 270func (s *reuseTokenSource) Token() (*Token, error) { 271 s.mu.Lock() 272 defer s.mu.Unlock() 273 if s.t.Valid() { 274 return s.t, nil 275 } 276 t, err := s.new.Token() 277 if err != nil { 278 return nil, err 279 } 280 s.t = t 281 return t, nil 282} 283 284// StaticTokenSource returns a TokenSource that always returns the same token. 285// Because the provided token t is never refreshed, StaticTokenSource is only 286// useful for tokens that never expire. 287func StaticTokenSource(t *Token) TokenSource { 288 return staticTokenSource{t} 289} 290 291// staticTokenSource is a TokenSource that always returns the same Token. 292type staticTokenSource struct { 293 t *Token 294} 295 296func (s staticTokenSource) Token() (*Token, error) { 297 return s.t, nil 298} 299 300// HTTPClient is the context key to use with golang.org/x/net/context's 301// WithValue function to associate an *http.Client value with a context. 302var HTTPClient internal.ContextKey 303 304// NewClient creates an *http.Client from a Context and TokenSource. 305// The returned client is not valid beyond the lifetime of the context. 306// 307// Note that if a custom *http.Client is provided via the Context it 308// is used only for token acquisition and is not used to configure the 309// *http.Client returned from NewClient. 310// 311// As a special case, if src is nil, a non-OAuth2 client is returned 312// using the provided context. This exists to support related OAuth2 313// packages. 314func NewClient(ctx context.Context, src TokenSource) *http.Client { 315 if src == nil { 316 return internal.ContextClient(ctx) 317 } 318 return &http.Client{ 319 Transport: &Transport{ 320 Base: internal.ContextClient(ctx).Transport, 321 Source: ReuseTokenSource(nil, src), 322 }, 323 } 324} 325 326// ReuseTokenSource returns a TokenSource which repeatedly returns the 327// same token as long as it's valid, starting with t. 328// When its cached token is invalid, a new token is obtained from src. 329// 330// ReuseTokenSource is typically used to reuse tokens from a cache 331// (such as a file on disk) between runs of a program, rather than 332// obtaining new tokens unnecessarily. 333// 334// The initial token t may be nil, in which case the TokenSource is 335// wrapped in a caching version if it isn't one already. This also 336// means it's always safe to wrap ReuseTokenSource around any other 337// TokenSource without adverse effects. 338func ReuseTokenSource(t *Token, src TokenSource) TokenSource { 339 // Don't wrap a reuseTokenSource in itself. That would work, 340 // but cause an unnecessary number of mutex operations. 341 // Just build the equivalent one. 342 if rt, ok := src.(*reuseTokenSource); ok { 343 if t == nil { 344 // Just use it directly. 345 return rt 346 } 347 src = rt.new 348 } 349 return &reuseTokenSource{ 350 t: t, 351 new: src, 352 } 353} 354