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