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