mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
basic OAuth2 support for remoteWrite and scrape targets (#1316)
* adds OAuth2 support for remoteWrite and scrapping * adds tests changes init
This commit is contained in:
parent
e05dd475f0
commit
5b8176c68e
11 changed files with 396 additions and 10 deletions
|
@ -44,6 +44,17 @@ var (
|
|||
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
|
||||
bearerToken = flagutil.NewArray("remoteWrite.bearerToken", "Optional bearer auth token to use for -remoteWrite.url. "+
|
||||
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
|
||||
|
||||
clientID = flagutil.NewArray("remoteWrite.oauth2.clientID", "Optional OAuth2 clientID to use for -remoteWrite.url."+
|
||||
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
|
||||
clientSecret = flagutil.NewArray("remoteWrite.oauth2.clientSecret", "Optional OAuth2 clientSecret to use for -remoteWrite.url."+
|
||||
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
|
||||
clientSecretFile = flagutil.NewArray("remoteWrite.oauth2.clientSecretFile", "Optional OAuth2 clientSecretFile to use for -remoteWrite.url."+
|
||||
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
|
||||
tokenURL = flagutil.NewArray("remoteWrite.oauth2.tokenUrl", "Optional OAuth2 token url to use for -remoteWrite.url."+
|
||||
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
|
||||
oAuth2Scopes = flagutil.NewArray("remoteWrite.oauth2.scopes", "Optional OAuth2 scopes to use for -remoteWrite.url."+
|
||||
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
|
||||
)
|
||||
|
||||
type client struct {
|
||||
|
@ -53,6 +64,8 @@ type client struct {
|
|||
fq *persistentqueue.FastQueue
|
||||
hc *http.Client
|
||||
|
||||
authCfg *promauth.Config
|
||||
|
||||
rl rateLimiter
|
||||
|
||||
bytesSent *metrics.Counter
|
||||
|
@ -72,6 +85,7 @@ func newClient(argIdx int, remoteWriteURL, sanitizedURL string, fq *persistentqu
|
|||
if err != nil {
|
||||
logger.Panicf("FATAL: cannot initialize TLS config: %s", err)
|
||||
}
|
||||
|
||||
tr := &http.Transport{
|
||||
Dial: statDial,
|
||||
TLSClientConfig: tlsCfg,
|
||||
|
@ -108,10 +122,18 @@ func newClient(argIdx int, remoteWriteURL, sanitizedURL string, fq *persistentqu
|
|||
}
|
||||
authHeader = "Bearer " + token
|
||||
}
|
||||
authCfg, err := getAuthConfig(argIdx)
|
||||
if err != nil {
|
||||
logger.Fatalf("FATAL: cannot create OAuth2 config for remoteWrite idx: %d, err: %s", argIdx, err)
|
||||
}
|
||||
if authCfg != nil && authHeader != "" {
|
||||
logger.Fatalf("`-remoteWrite.bearerToken`=%q or `-remoteWrite.basicAuth.* cannot be set when `-remoteWrite.oauth2.*` flags are set", token)
|
||||
}
|
||||
c := &client{
|
||||
sanitizedURL: sanitizedURL,
|
||||
remoteWriteURL: remoteWriteURL,
|
||||
authHeader: authHeader,
|
||||
authCfg: authCfg,
|
||||
fq: fq,
|
||||
hc: &http.Client{
|
||||
Transport: tr,
|
||||
|
@ -160,7 +182,7 @@ func getTLSConfig(argIdx int) (*tls.Config, error) {
|
|||
if c.CAFile == "" && c.CertFile == "" && c.KeyFile == "" && c.ServerName == "" && !c.InsecureSkipVerify {
|
||||
return nil, nil
|
||||
}
|
||||
cfg, err := promauth.NewConfig(".", nil, nil, "", "", c)
|
||||
cfg, err := promauth.NewConfig(".", nil, nil, "", "", nil, c)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot populate TLS config: %w", err)
|
||||
}
|
||||
|
@ -168,6 +190,25 @@ func getTLSConfig(argIdx int) (*tls.Config, error) {
|
|||
return tlsCfg, nil
|
||||
}
|
||||
|
||||
func getAuthConfig(argIdx int) (*promauth.Config, error) {
|
||||
|
||||
oAuth2Cfg := &promauth.OAuth2Config{
|
||||
ClientID: clientID.GetOptionalArg(argIdx),
|
||||
ClientSecret: clientSecret.GetOptionalArg(argIdx),
|
||||
ClientSecretFile: clientSecretFile.GetOptionalArg(argIdx),
|
||||
TokenURL: tokenURL.GetOptionalArg(argIdx),
|
||||
Scopes: strings.Split(oAuth2Scopes.GetOptionalArg(argIdx), ";"),
|
||||
}
|
||||
if oAuth2Cfg.ClientSecretFile == "" && oAuth2Cfg.ClientSecret == "" {
|
||||
return nil, nil
|
||||
}
|
||||
authCfg, err := promauth.NewConfig("", nil, nil, "", "", oAuth2Cfg, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot populate OAuth2 config for remoteWrite idx: %d, err: %w", argIdx, err)
|
||||
}
|
||||
return authCfg, nil
|
||||
}
|
||||
|
||||
func (c *client) runWorker() {
|
||||
var ok bool
|
||||
var block []byte
|
||||
|
@ -229,6 +270,11 @@ again:
|
|||
if c.authHeader != "" {
|
||||
req.Header.Set("Authorization", c.authHeader)
|
||||
}
|
||||
// add oauth2 header on best effort.
|
||||
// remote storage may return error with incorrect authorization.
|
||||
if c.authCfg != nil {
|
||||
req.Header.Set("Authorization", c.authCfg.GetAuthHeader())
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
resp, err := c.hc.Do(req)
|
||||
|
|
2
go.mod
2
go.mod
|
@ -31,7 +31,7 @@ require (
|
|||
github.com/valyala/gozstd v1.10.0
|
||||
github.com/valyala/histogram v1.1.2
|
||||
github.com/valyala/quicktemplate v1.6.3
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed
|
||||
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015
|
||||
google.golang.org/api v0.47.0
|
||||
|
|
4
go.sum
4
go.sum
|
@ -533,7 +533,7 @@ github.com/influxdata/flux v0.113.0/go.mod h1:3TJtvbm/Kwuo5/PEo5P6HUzwVg4bXWkb2w
|
|||
github.com/influxdata/httprouter v1.3.1-0.20191122104820-ee83e2772f69/go.mod h1:pwymjR6SrP3gD3pRj9RJwdl1j5s3doEEV8gS4X9qSzA=
|
||||
github.com/influxdata/influxdb v1.8.0/go.mod h1:SIzcnsjaHRFpmlxpJ4S3NT64qtEKYweNTUMb/vh0OMQ=
|
||||
github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI=
|
||||
github.com/influxdata/influxdb v1.9.0 h1:9z/aRmTpWT1rIm4EN+qTJTZqgEdLGZ4xRMgvA276UEA=
|
||||
github.com/influxdata/influxdb v1.9.0 h1:KefL3i2JdNgsZwKRlHkkV+sYs4ejJ+aGF6glBUoKKio=
|
||||
github.com/influxdata/influxdb v1.9.0/go.mod h1:UEe3MeD9AaP5rlPIes102IhYua3FhIWZuOXNHxDjSrI=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/influxdata/influxql v1.1.0/go.mod h1:KpVI7okXjK6PRi3Z5B+mtKZli+R1DnZgb3N+tzevNgo=
|
||||
|
@ -1033,6 +1033,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
|||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE=
|
||||
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
|
|
@ -2,6 +2,7 @@ package promauth
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
|
@ -11,6 +12,8 @@ import (
|
|||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/clientcredentials"
|
||||
)
|
||||
|
||||
// TLSConfig represents TLS config.
|
||||
|
@ -46,6 +49,7 @@ type HTTPClientConfig struct {
|
|||
BasicAuth *BasicAuthConfig `yaml:"basic_auth,omitempty"`
|
||||
BearerToken string `yaml:"bearer_token,omitempty"`
|
||||
BearerTokenFile string `yaml:"bearer_token_file,omitempty"`
|
||||
OAuth2 *OAuth2Config `yaml:"oauth2,omitempty"`
|
||||
TLSConfig *TLSConfig `yaml:"tls_config,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -58,6 +62,69 @@ type ProxyClientConfig struct {
|
|||
TLSConfig *TLSConfig `yaml:"proxy_tls_config,omitempty"`
|
||||
}
|
||||
|
||||
// OAuth2Config represent OAuth2 configuration
|
||||
type OAuth2Config struct {
|
||||
ClientID string `yaml:"client_id"`
|
||||
ClientSecretFile string `yaml:"client_secret_file"`
|
||||
Scopes []string `yaml:"scopes"`
|
||||
TokenURL string `yaml:"token_url"`
|
||||
// mu guards tokenSource and client Secret
|
||||
mu sync.Mutex
|
||||
ClientSecret string `yaml:"client_secret"`
|
||||
tokenSource oauth2.TokenSource
|
||||
}
|
||||
|
||||
func (o *OAuth2Config) refreshTokenSourceLocked() {
|
||||
cfg := clientcredentials.Config{
|
||||
ClientID: o.ClientID,
|
||||
ClientSecret: o.ClientSecret,
|
||||
TokenURL: o.TokenURL,
|
||||
Scopes: o.Scopes,
|
||||
}
|
||||
o.tokenSource = cfg.TokenSource(context.Background())
|
||||
}
|
||||
|
||||
// validate checks given configs.
|
||||
func (o *OAuth2Config) validate() error {
|
||||
if o.TokenURL == "" {
|
||||
return fmt.Errorf("token url cannot be empty")
|
||||
}
|
||||
if o.ClientSecret == "" && o.ClientSecretFile == "" {
|
||||
return fmt.Errorf("ClientSecret or ClientSecretFile must be set")
|
||||
}
|
||||
if o.ClientSecret != "" && o.ClientSecretFile != "" {
|
||||
return fmt.Errorf("only one option can be set ClientSecret or ClientSecretFile, provided both")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *OAuth2Config) getAuthHeader() (string, error) {
|
||||
var needUpdate bool
|
||||
if o.ClientSecretFile != "" {
|
||||
newSecret, err := readPasswordFromFile(o.ClientSecretFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot read OAuth2 config file with path: %s, err: %w", o.ClientSecretFile, err)
|
||||
}
|
||||
o.mu.Lock()
|
||||
if o.ClientSecret != newSecret {
|
||||
o.ClientSecret = newSecret
|
||||
needUpdate = true
|
||||
}
|
||||
o.mu.Unlock()
|
||||
}
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
if needUpdate {
|
||||
o.refreshTokenSourceLocked()
|
||||
}
|
||||
t, err := o.tokenSource.Token()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot fetch token for OAuth2 client: %w", err)
|
||||
}
|
||||
|
||||
return t.Type() + " " + t.AccessToken, nil
|
||||
}
|
||||
|
||||
// Config is auth config.
|
||||
type Config struct {
|
||||
// Optional TLS config
|
||||
|
@ -131,16 +198,16 @@ func (ac *Config) NewTLSConfig() *tls.Config {
|
|||
|
||||
// NewConfig creates auth config for the given hcc.
|
||||
func (hcc *HTTPClientConfig) NewConfig(baseDir string) (*Config, error) {
|
||||
return NewConfig(baseDir, hcc.Authorization, hcc.BasicAuth, hcc.BearerToken, hcc.BearerTokenFile, hcc.TLSConfig)
|
||||
return NewConfig(baseDir, hcc.Authorization, hcc.BasicAuth, hcc.BearerToken, hcc.BearerTokenFile, hcc.OAuth2, hcc.TLSConfig)
|
||||
}
|
||||
|
||||
// NewConfig creates auth config for the given pcc.
|
||||
func (pcc *ProxyClientConfig) NewConfig(baseDir string) (*Config, error) {
|
||||
return NewConfig(baseDir, pcc.Authorization, pcc.BasicAuth, pcc.BearerToken, pcc.BearerTokenFile, pcc.TLSConfig)
|
||||
return NewConfig(baseDir, pcc.Authorization, pcc.BasicAuth, pcc.BearerToken, pcc.BearerTokenFile, nil, pcc.TLSConfig)
|
||||
}
|
||||
|
||||
// NewConfig creates auth config from the given args.
|
||||
func NewConfig(baseDir string, az *Authorization, basicAuth *BasicAuthConfig, bearerToken, bearerTokenFile string, tlsConfig *TLSConfig) (*Config, error) {
|
||||
func NewConfig(baseDir string, az *Authorization, basicAuth *BasicAuthConfig, bearerToken, bearerTokenFile string, oauth *OAuth2Config, tlsConfig *TLSConfig) (*Config, error) {
|
||||
var getAuthHeader func() string
|
||||
authDigest := ""
|
||||
if az != nil {
|
||||
|
@ -230,6 +297,30 @@ func NewConfig(baseDir string, az *Authorization, basicAuth *BasicAuthConfig, be
|
|||
}
|
||||
authDigest = fmt.Sprintf("bearer(token=%q)", bearerToken)
|
||||
}
|
||||
if oauth != nil {
|
||||
if getAuthHeader != nil {
|
||||
return nil, fmt.Errorf("cannot simultaneously use `authorization`, `basic_auth, `bearer_token` and `ouath2`")
|
||||
}
|
||||
if err := oauth.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if oauth.ClientSecretFile != "" {
|
||||
secret, err := readPasswordFromFile(oauth.ClientSecretFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oauth.ClientSecret = secret
|
||||
}
|
||||
oauth.refreshTokenSourceLocked()
|
||||
getAuthHeader = func() string {
|
||||
h, err := oauth.getAuthHeader()
|
||||
if err != nil {
|
||||
logger.Errorf("cannot get OAuth2 header: %s", err)
|
||||
return ""
|
||||
}
|
||||
return h
|
||||
}
|
||||
}
|
||||
var tlsRootCA *x509.CertPool
|
||||
var tlsCertificate *tls.Certificate
|
||||
tlsServerName := ""
|
||||
|
|
125
lib/promauth/config_test.go
Normal file
125
lib/promauth/config_test.go
Normal file
|
@ -0,0 +1,125 @@
|
|||
package promauth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewConfig(t *testing.T) {
|
||||
type args struct {
|
||||
baseDir string
|
||||
az *Authorization
|
||||
basicAuth *BasicAuthConfig
|
||||
bearerToken string
|
||||
bearerTokenFile string
|
||||
oauth *OAuth2Config
|
||||
tlsConfig *TLSConfig
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "OAuth2 config",
|
||||
args: args{
|
||||
oauth: &OAuth2Config{
|
||||
ClientID: "some-id",
|
||||
ClientSecret: "some-secret",
|
||||
TokenURL: "http://localhost:8511",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "OAuth2 config with file",
|
||||
args: args{
|
||||
oauth: &OAuth2Config{
|
||||
ClientID: "some-id",
|
||||
ClientSecretFile: "testdata/test_secretfile.txt",
|
||||
TokenURL: "http://localhost:8511",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "OAuth2 want err",
|
||||
args: args{
|
||||
oauth: &OAuth2Config{
|
||||
ClientID: "some-id",
|
||||
ClientSecret: "some-secret",
|
||||
ClientSecretFile: "testdata/test_secretfile.txt",
|
||||
TokenURL: "http://localhost:8511",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "basic Auth config",
|
||||
args: args{
|
||||
basicAuth: &BasicAuthConfig{
|
||||
Username: "user",
|
||||
Password: "password",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "basic Auth config with file",
|
||||
args: args{
|
||||
basicAuth: &BasicAuthConfig{
|
||||
Username: "user",
|
||||
PasswordFile: "testdata/test_secretfile.txt",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "want Authorization",
|
||||
args: args{
|
||||
az: &Authorization{
|
||||
Type: "Bearer ",
|
||||
Credentials: "Value",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "token file",
|
||||
args: args{
|
||||
bearerTokenFile: "testdata/test_secretfile.txt",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "token with tls",
|
||||
args: args{
|
||||
bearerToken: "some-token",
|
||||
tlsConfig: &TLSConfig{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.oauth != nil {
|
||||
r := http.NewServeMux()
|
||||
r.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{"access_token":"some-token","token_type": "Bearer "}`))
|
||||
|
||||
})
|
||||
mock := httptest.NewServer(r)
|
||||
tt.args.oauth.TokenURL = mock.URL
|
||||
}
|
||||
got, err := NewConfig(tt.args.baseDir, tt.args.az, tt.args.basicAuth, tt.args.bearerToken, tt.args.bearerTokenFile, tt.args.oauth, tt.args.tlsConfig)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NewConfig() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != nil {
|
||||
if ah := got.GetAuthHeader(); ah == "" {
|
||||
t.Fatalf("unexpected empty auth header")
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
1
lib/promauth/testdata/test_secretfile.txt
vendored
Normal file
1
lib/promauth/testdata/test_secretfile.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
secret-content
|
|
@ -50,7 +50,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) {
|
|||
}
|
||||
token = ""
|
||||
}
|
||||
ac, err := promauth.NewConfig(baseDir, nil, ba, token, "", sdc.TLSConfig)
|
||||
ac, err := promauth.NewConfig(baseDir, nil, ba, token, "", nil, sdc.TLSConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse auth config: %w", err)
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string, swcFunc ScrapeWorkConstructorFu
|
|||
tlsConfig := promauth.TLSConfig{
|
||||
CAFile: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt",
|
||||
}
|
||||
acNew, err := promauth.NewConfig(".", nil, nil, "", "/var/run/secrets/kubernetes.io/serviceaccount/token", &tlsConfig)
|
||||
acNew, err := promauth.NewConfig(".", nil, nil, "", "/var/run/secrets/kubernetes.io/serviceaccount/token", nil, &tlsConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot initialize service account auth: %w; probably, `kubernetes_sd_config->api_server` is missing in Prometheus configs?", err)
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) {
|
|||
port: sdc.Port,
|
||||
}
|
||||
if sdc.TLSConfig != nil {
|
||||
ac, err := promauth.NewConfig(baseDir, nil, nil, "", "", sdc.TLSConfig)
|
||||
ac, err := promauth.NewConfig(baseDir, nil, nil, "", "", nil, sdc.TLSConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
120
vendor/golang.org/x/oauth2/clientcredentials/clientcredentials.go
generated
vendored
Normal file
120
vendor/golang.org/x/oauth2/clientcredentials/clientcredentials.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package clientcredentials implements the OAuth2.0 "client credentials" token flow,
|
||||
// also known as the "two-legged OAuth 2.0".
|
||||
//
|
||||
// This should be used when the client is acting on its own behalf or when the client
|
||||
// is the resource owner. It may also be used when requesting access to protected
|
||||
// resources based on an authorization previously arranged with the authorization
|
||||
// server.
|
||||
//
|
||||
// See https://tools.ietf.org/html/rfc6749#section-4.4
|
||||
package clientcredentials // import "golang.org/x/oauth2/clientcredentials"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/internal"
|
||||
)
|
||||
|
||||
// Config describes a 2-legged OAuth2 flow, with both the
|
||||
// client application information and the server's endpoint URLs.
|
||||
type Config struct {
|
||||
// ClientID is the application's ID.
|
||||
ClientID string
|
||||
|
||||
// ClientSecret is the application's secret.
|
||||
ClientSecret string
|
||||
|
||||
// TokenURL is the resource server's token endpoint
|
||||
// URL. This is a constant specific to each server.
|
||||
TokenURL string
|
||||
|
||||
// Scope specifies optional requested permissions.
|
||||
Scopes []string
|
||||
|
||||
// EndpointParams specifies additional parameters for requests to the token endpoint.
|
||||
EndpointParams url.Values
|
||||
|
||||
// AuthStyle optionally specifies how the endpoint wants the
|
||||
// client ID & client secret sent. The zero value means to
|
||||
// auto-detect.
|
||||
AuthStyle oauth2.AuthStyle
|
||||
}
|
||||
|
||||
// Token uses client credentials to retrieve a token.
|
||||
//
|
||||
// The provided context optionally controls which HTTP client is used. See the oauth2.HTTPClient variable.
|
||||
func (c *Config) Token(ctx context.Context) (*oauth2.Token, error) {
|
||||
return c.TokenSource(ctx).Token()
|
||||
}
|
||||
|
||||
// Client returns an HTTP client using the provided token.
|
||||
// The token will auto-refresh as necessary.
|
||||
//
|
||||
// The provided context optionally controls which HTTP client
|
||||
// is returned. See the oauth2.HTTPClient variable.
|
||||
//
|
||||
// The returned Client and its Transport should not be modified.
|
||||
func (c *Config) Client(ctx context.Context) *http.Client {
|
||||
return oauth2.NewClient(ctx, c.TokenSource(ctx))
|
||||
}
|
||||
|
||||
// TokenSource returns a TokenSource that returns t until t expires,
|
||||
// automatically refreshing it as necessary using the provided context and the
|
||||
// client ID and client secret.
|
||||
//
|
||||
// Most users will use Config.Client instead.
|
||||
func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource {
|
||||
source := &tokenSource{
|
||||
ctx: ctx,
|
||||
conf: c,
|
||||
}
|
||||
return oauth2.ReuseTokenSource(nil, source)
|
||||
}
|
||||
|
||||
type tokenSource struct {
|
||||
ctx context.Context
|
||||
conf *Config
|
||||
}
|
||||
|
||||
// Token refreshes the token by using a new client credentials request.
|
||||
// tokens received this way do not include a refresh token
|
||||
func (c *tokenSource) Token() (*oauth2.Token, error) {
|
||||
v := url.Values{
|
||||
"grant_type": {"client_credentials"},
|
||||
}
|
||||
if len(c.conf.Scopes) > 0 {
|
||||
v.Set("scope", strings.Join(c.conf.Scopes, " "))
|
||||
}
|
||||
for k, p := range c.conf.EndpointParams {
|
||||
// Allow grant_type to be overridden to allow interoperability with
|
||||
// non-compliant implementations.
|
||||
if _, ok := v[k]; ok && k != "grant_type" {
|
||||
return nil, fmt.Errorf("oauth2: cannot overwrite parameter %q", k)
|
||||
}
|
||||
v[k] = p
|
||||
}
|
||||
|
||||
tk, err := internal.RetrieveToken(c.ctx, c.conf.ClientID, c.conf.ClientSecret, c.conf.TokenURL, v, internal.AuthStyle(c.conf.AuthStyle))
|
||||
if err != nil {
|
||||
if rErr, ok := err.(*internal.RetrieveError); ok {
|
||||
return nil, (*oauth2.RetrieveError)(rErr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
t := &oauth2.Token{
|
||||
AccessToken: tk.AccessToken,
|
||||
TokenType: tk.TokenType,
|
||||
RefreshToken: tk.RefreshToken,
|
||||
Expiry: tk.Expiry,
|
||||
}
|
||||
return t.WithExtra(tk.Raw), nil
|
||||
}
|
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
|
@ -235,7 +235,7 @@ golang.org/x/lint/golint
|
|||
# golang.org/x/mod v0.4.2
|
||||
golang.org/x/mod/module
|
||||
golang.org/x/mod/semver
|
||||
# golang.org/x/net v0.0.0-20210510120150-4163338589ed
|
||||
# golang.org/x/net v0.0.0-20210520170846-37e1c6afe023
|
||||
## explicit
|
||||
golang.org/x/net/context
|
||||
golang.org/x/net/context/ctxhttp
|
||||
|
@ -251,6 +251,7 @@ golang.org/x/net/trace
|
|||
## explicit
|
||||
golang.org/x/oauth2
|
||||
golang.org/x/oauth2/authhandler
|
||||
golang.org/x/oauth2/clientcredentials
|
||||
golang.org/x/oauth2/google
|
||||
golang.org/x/oauth2/google/internal/externalaccount
|
||||
golang.org/x/oauth2/internal
|
||||
|
|
Loading…
Reference in a new issue