clientcredentials.go raw

   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