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 clientcredentials implements the OAuth2.0 "client credentials" token flow,
6 // also known as "two-legged OAuth 2.0".
7 //
8 // This should be used when the client is acting on its own behalf or when the client
9 // is the resource owner. It may also be used when requesting access to protected
10 // resources based on an authorization previously arranged with the authorization
11 // server.
12 //
13 // See https://tools.ietf.org/html/rfc6749#section-4.4
14 package clientcredentials // import "golang.org/x/oauth2/clientcredentials"
15 16 import (
17 "context"
18 "fmt"
19 "net/http"
20 "net/url"
21 "strings"
22 23 "golang.org/x/oauth2"
24 "golang.org/x/oauth2/internal"
25 )
26 27 // Config describes a 2-legged OAuth2 flow, with both the
28 // client application information and the server's endpoint URLs.
29 type Config struct {
30 // ClientID is the application's ID.
31 ClientID string
32 33 // ClientSecret is the application's secret.
34 ClientSecret string
35 36 // TokenURL is the resource server's token endpoint
37 // URL. This is a constant specific to each server.
38 TokenURL string
39 40 // Scopes specifies optional requested permissions.
41 Scopes []string
42 43 // EndpointParams specifies additional parameters for requests to the token endpoint.
44 EndpointParams url.Values
45 46 // AuthStyle optionally specifies how the endpoint wants the
47 // client ID & client secret sent. The zero value means to
48 // auto-detect.
49 AuthStyle oauth2.AuthStyle
50 51 // authStyleCache caches which auth style to use when Endpoint.AuthStyle is
52 // the zero value (AuthStyleAutoDetect).
53 authStyleCache internal.LazyAuthStyleCache
54 }
55 56 // Token uses client credentials to retrieve a token.
57 //
58 // The provided context optionally controls which HTTP client is used. See the [oauth2.HTTPClient] variable.
59 func (c *Config) Token(ctx context.Context) (*oauth2.Token, error) {
60 return c.TokenSource(ctx).Token()
61 }
62 63 // Client returns an HTTP client using the provided token.
64 // The token will auto-refresh as necessary.
65 //
66 // The provided context optionally controls which HTTP client
67 // is returned. See the [oauth2.HTTPClient] variable.
68 //
69 // The returned [http.Client] and its Transport should not be modified.
70 func (c *Config) Client(ctx context.Context) *http.Client {
71 return oauth2.NewClient(ctx, c.TokenSource(ctx))
72 }
73 74 // TokenSource returns a [oauth2.TokenSource] that returns t until t expires,
75 // automatically refreshing it as necessary using the provided context and the
76 // client ID and client secret.
77 //
78 // Most users will use [Config.Client] instead.
79 func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource {
80 source := &tokenSource{
81 ctx: ctx,
82 conf: c,
83 }
84 return oauth2.ReuseTokenSource(nil, source)
85 }
86 87 type tokenSource struct {
88 ctx context.Context
89 conf *Config
90 }
91 92 // Token refreshes the token by using a new client credentials request.
93 // tokens received this way do not include a refresh token
94 func (c *tokenSource) Token() (*oauth2.Token, error) {
95 v := url.Values{
96 "grant_type": {"client_credentials"},
97 }
98 if len(c.conf.Scopes) > 0 {
99 v.Set("scope", strings.Join(c.conf.Scopes, " "))
100 }
101 for k, p := range c.conf.EndpointParams {
102 // Allow grant_type to be overridden to allow interoperability with
103 // non-compliant implementations.
104 if _, ok := v[k]; ok && k != "grant_type" {
105 return nil, fmt.Errorf("oauth2: cannot overwrite parameter %q", k)
106 }
107 v[k] = p
108 }
109 110 tk, err := internal.RetrieveToken(c.ctx, c.conf.ClientID, c.conf.ClientSecret, c.conf.TokenURL, v, internal.AuthStyle(c.conf.AuthStyle), c.conf.authStyleCache.Get())
111 if err != nil {
112 if rErr, ok := err.(*internal.RetrieveError); ok {
113 return nil, (*oauth2.RetrieveError)(rErr)
114 }
115 return nil, err
116 }
117 t := &oauth2.Token{
118 AccessToken: tk.AccessToken,
119 TokenType: tk.TokenType,
120 RefreshToken: tk.RefreshToken,
121 Expiry: tk.Expiry,
122 }
123 return t.WithExtra(tk.Raw), nil
124 }
125