You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

121 line
3.7 KiB

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