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 "context"
13 "errors"
14 "net/http"
15 "net/url"
16 "strings"
17 "sync"
18 "time"
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].
25 //
26 // Deprecated: Use [context.Background] or [context.TODO] instead.
27 var 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.
35 func 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
40 // [golang.org/x/oauth2/clientcredentials] package.
41 type 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 authorization 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 // Scopes specifies optional requested permissions.
59 Scopes []string
60 61 // authStyleCache caches which auth style to use when Endpoint.AuthStyle is
62 // the zero value (AuthStyleAutoDetect).
63 authStyleCache internal.LazyAuthStyleCache
64 }
65 66 // A TokenSource is anything that can return a token.
67 type 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.
76 type Endpoint struct {
77 AuthURL string
78 DeviceAuthURL string
79 TokenURL string
80 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.
89 type AuthStyle int
90 91 const (
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_secret
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 107 var (
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.
129 type AuthCodeOption interface {
130 setValue(url.Values)
131 }
132 133 type setParam struct{ k, v string }
134 135 func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) }
136 137 // SetAuthURLParam builds an [AuthCodeOption] which passes key/value parameters
138 // to a provider's authorization endpoint.
139 func 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 //
146 // 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.
149 //
150 // 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)
159 func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
160 var buf strings.Builder
161 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 != "" {
173 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 //
196 // The provided context optionally controls which HTTP client is used. See the [HTTPClient] variable.
197 func (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 //
214 // The provided context optionally controls which HTTP client is used. See the [HTTPClient] variable.
215 //
216 // 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.
219 //
220 // If using PKCE to protect against CSRF attacks, opts should include a
221 // VerifierOption.
222 func (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.
240 func (c *Config) Client(ctx context.Context, t *Token) *http.Client {
241 return NewClient(ctx, c.TokenSource(ctx, t))
242 }
243 244 // TokenSource returns a [TokenSource] that returns t until t expires,
245 // automatically refreshing it as necessary using the provided context.
246 //
247 // Most users will use [Config.Client] instead.
248 func (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 262 // tokenRefresher is a TokenSource that makes "grant_type=refresh_token"
263 // HTTP requests to renew a token using a RefreshToken.
264 type 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.
274 func (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 }
290 return tk, nil
291 }
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.
297 type reuseTokenSource struct {
298 new TokenSource // called when t is expired.
299 300 mu sync.Mutex // guards t
301 t *Token
302 303 expiryDelta time.Duration
304 }
305 306 // Token returns the current token if it's still valid, else will
307 // refresh the current token and return the new one.
308 func (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 }
318 t.expiryDelta = s.expiryDelta
319 s.t = t
320 return t, nil
321 }
322 323 // StaticTokenSource returns a [TokenSource] that always returns the same token.
324 // Because the provided token t is never refreshed, StaticTokenSource is only
325 // useful for tokens that never expire.
326 func StaticTokenSource(t *Token) TokenSource {
327 return staticTokenSource{t}
328 }
329 330 // staticTokenSource is a TokenSource that always returns the same Token.
331 type staticTokenSource struct {
332 t *Token
333 }
334 335 func (s staticTokenSource) Token() (*Token, error) {
336 return s.t, nil
337 }
338 339 // HTTPClient is the context key to use with [context.WithValue]
340 // to associate a [*http.Client] value with a context.
341 var HTTPClient internal.ContextKey
342 343 // NewClient creates an [*http.Client] from a [context.Context] and [TokenSource].
344 // The returned client is not valid beyond the lifetime of the context.
345 //
346 // Note that if a custom [*http.Client] is provided via the [context.Context] it
347 // is used only for token acquisition and is not used to configure the
348 // [*http.Client] returned from NewClient.
349 //
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.
353 func NewClient(ctx context.Context, src TokenSource) *http.Client {
354 if src == nil {
355 return internal.ContextClient(ctx)
356 }
357 cc := internal.ContextClient(ctx)
358 return &http.Client{
359 Transport: &Transport{
360 Base: cc.Transport,
361 Source: ReuseTokenSource(nil, src),
362 },
363 CheckRedirect: cc.CheckRedirect,
364 Jar: cc.Jar,
365 Timeout: cc.Timeout,
366 }
367 }
368 369 // ReuseTokenSource returns a [TokenSource] which repeatedly returns the
370 // 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 //
377 // The initial token t may be nil, in which case the [TokenSource] is
378 // 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
380 // [TokenSource] without adverse effects.
381 func 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 }
397 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).
402 func 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 }
424