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