[VOL-5603]:Metrics fix,update protos in voltctl
Signed-off-by: balaji.nagarajan <balaji.nagarajan@radisys.com>
Change-Id: I92702118bef90623ef7c68c646c86634e60d889d
diff --git a/vendor/golang.org/x/oauth2/LICENSE b/vendor/golang.org/x/oauth2/LICENSE
index 6a66aea..2a7cf70 100644
--- a/vendor/golang.org/x/oauth2/LICENSE
+++ b/vendor/golang.org/x/oauth2/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2009 The Go Authors. All rights reserved.
+Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -10,7 +10,7 @@
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
- * Neither the name of Google Inc. nor the names of its
+ * Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
diff --git a/vendor/golang.org/x/oauth2/README.md b/vendor/golang.org/x/oauth2/README.md
index 1473e12..48dbb9d 100644
--- a/vendor/golang.org/x/oauth2/README.md
+++ b/vendor/golang.org/x/oauth2/README.md
@@ -5,21 +5,12 @@
oauth2 package contains a client implementation for OAuth 2.0 spec.
-## Installation
-
-~~~~
-go get golang.org/x/oauth2
-~~~~
-
-Or you can manually git clone the repository to
-`$(go env GOPATH)/src/golang.org/x/oauth2`.
-
See pkg.go.dev for further documentation and examples.
* [pkg.go.dev/golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2)
* [pkg.go.dev/golang.org/x/oauth2/google](https://pkg.go.dev/golang.org/x/oauth2/google)
-## Policy for new packages
+## Policy for new endpoints
We no longer accept new provider-specific packages in this repo if all
they do is add a single endpoint variable. If you just want to add a
@@ -29,8 +20,16 @@
## Report Issues / Send Patches
-This repository uses Gerrit for code changes. To learn how to submit changes to
-this repository, see https://golang.org/doc/contribute.html.
-
The main issue tracker for the oauth2 repository is located at
https://github.com/golang/oauth2/issues.
+
+This repository uses Gerrit for code changes. To learn how to submit changes to
+this repository, see https://go.dev/doc/contribute.
+
+The git repository is https://go.googlesource.com/oauth2.
+
+Note:
+
+* Excluding trivial changes, all contributions should be connected to an existing issue.
+* API changes must go through the [change proposal process](https://go.dev/s/proposal-process) before they can be accepted.
+* The code owners are listed at [dev.golang.org/owners](https://dev.golang.org/owners#:~:text=x/oauth2).
diff --git a/vendor/golang.org/x/oauth2/deviceauth.go b/vendor/golang.org/x/oauth2/deviceauth.go
new file mode 100644
index 0000000..e99c92f
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/deviceauth.go
@@ -0,0 +1,198 @@
+package oauth2
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+
+ "golang.org/x/oauth2/internal"
+)
+
+// https://datatracker.ietf.org/doc/html/rfc8628#section-3.5
+const (
+ errAuthorizationPending = "authorization_pending"
+ errSlowDown = "slow_down"
+ errAccessDenied = "access_denied"
+ errExpiredToken = "expired_token"
+)
+
+// DeviceAuthResponse describes a successful RFC 8628 Device Authorization Response
+// https://datatracker.ietf.org/doc/html/rfc8628#section-3.2
+type DeviceAuthResponse struct {
+ // DeviceCode
+ DeviceCode string `json:"device_code"`
+ // UserCode is the code the user should enter at the verification uri
+ UserCode string `json:"user_code"`
+ // VerificationURI is where user should enter the user code
+ VerificationURI string `json:"verification_uri"`
+ // VerificationURIComplete (if populated) includes the user code in the verification URI. This is typically shown to the user in non-textual form, such as a QR code.
+ VerificationURIComplete string `json:"verification_uri_complete,omitempty"`
+ // Expiry is when the device code and user code expire
+ Expiry time.Time `json:"expires_in,omitempty"`
+ // Interval is the duration in seconds that Poll should wait between requests
+ Interval int64 `json:"interval,omitempty"`
+}
+
+func (d DeviceAuthResponse) MarshalJSON() ([]byte, error) {
+ type Alias DeviceAuthResponse
+ var expiresIn int64
+ if !d.Expiry.IsZero() {
+ expiresIn = int64(time.Until(d.Expiry).Seconds())
+ }
+ return json.Marshal(&struct {
+ ExpiresIn int64 `json:"expires_in,omitempty"`
+ *Alias
+ }{
+ ExpiresIn: expiresIn,
+ Alias: (*Alias)(&d),
+ })
+
+}
+
+func (c *DeviceAuthResponse) UnmarshalJSON(data []byte) error {
+ type Alias DeviceAuthResponse
+ aux := &struct {
+ ExpiresIn int64 `json:"expires_in"`
+ // workaround misspelling of verification_uri
+ VerificationURL string `json:"verification_url"`
+ *Alias
+ }{
+ Alias: (*Alias)(c),
+ }
+ if err := json.Unmarshal(data, &aux); err != nil {
+ return err
+ }
+ if aux.ExpiresIn != 0 {
+ c.Expiry = time.Now().UTC().Add(time.Second * time.Duration(aux.ExpiresIn))
+ }
+ if c.VerificationURI == "" {
+ c.VerificationURI = aux.VerificationURL
+ }
+ return nil
+}
+
+// DeviceAuth returns a device auth struct which contains a device code
+// and authorization information provided for users to enter on another device.
+func (c *Config) DeviceAuth(ctx context.Context, opts ...AuthCodeOption) (*DeviceAuthResponse, error) {
+ // https://datatracker.ietf.org/doc/html/rfc8628#section-3.1
+ v := url.Values{
+ "client_id": {c.ClientID},
+ }
+ if len(c.Scopes) > 0 {
+ v.Set("scope", strings.Join(c.Scopes, " "))
+ }
+ for _, opt := range opts {
+ opt.setValue(v)
+ }
+ return retrieveDeviceAuth(ctx, c, v)
+}
+
+func retrieveDeviceAuth(ctx context.Context, c *Config, v url.Values) (*DeviceAuthResponse, error) {
+ if c.Endpoint.DeviceAuthURL == "" {
+ return nil, errors.New("endpoint missing DeviceAuthURL")
+ }
+
+ req, err := http.NewRequest("POST", c.Endpoint.DeviceAuthURL, strings.NewReader(v.Encode()))
+ if err != nil {
+ return nil, err
+ }
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ req.Header.Set("Accept", "application/json")
+
+ t := time.Now()
+ r, err := internal.ContextClient(ctx).Do(req)
+ if err != nil {
+ return nil, err
+ }
+
+ body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20))
+ if err != nil {
+ return nil, fmt.Errorf("oauth2: cannot auth device: %v", err)
+ }
+ if code := r.StatusCode; code < 200 || code > 299 {
+ return nil, &RetrieveError{
+ Response: r,
+ Body: body,
+ }
+ }
+
+ da := &DeviceAuthResponse{}
+ err = json.Unmarshal(body, &da)
+ if err != nil {
+ return nil, fmt.Errorf("unmarshal %s", err)
+ }
+
+ if !da.Expiry.IsZero() {
+ // Make a small adjustment to account for time taken by the request
+ da.Expiry = da.Expiry.Add(-time.Since(t))
+ }
+
+ return da, nil
+}
+
+// DeviceAccessToken polls the server to exchange a device code for a token.
+func (c *Config) DeviceAccessToken(ctx context.Context, da *DeviceAuthResponse, opts ...AuthCodeOption) (*Token, error) {
+ if !da.Expiry.IsZero() {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithDeadline(ctx, da.Expiry)
+ defer cancel()
+ }
+
+ // https://datatracker.ietf.org/doc/html/rfc8628#section-3.4
+ v := url.Values{
+ "client_id": {c.ClientID},
+ "grant_type": {"urn:ietf:params:oauth:grant-type:device_code"},
+ "device_code": {da.DeviceCode},
+ }
+ if len(c.Scopes) > 0 {
+ v.Set("scope", strings.Join(c.Scopes, " "))
+ }
+ for _, opt := range opts {
+ opt.setValue(v)
+ }
+
+ // "If no value is provided, clients MUST use 5 as the default."
+ // https://datatracker.ietf.org/doc/html/rfc8628#section-3.2
+ interval := da.Interval
+ if interval == 0 {
+ interval = 5
+ }
+
+ ticker := time.NewTicker(time.Duration(interval) * time.Second)
+ defer ticker.Stop()
+ for {
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ case <-ticker.C:
+ tok, err := retrieveToken(ctx, c, v)
+ if err == nil {
+ return tok, nil
+ }
+
+ e, ok := err.(*RetrieveError)
+ if !ok {
+ return nil, err
+ }
+ switch e.ErrorCode {
+ case errSlowDown:
+ // https://datatracker.ietf.org/doc/html/rfc8628#section-3.5
+ // "the interval MUST be increased by 5 seconds for this and all subsequent requests"
+ interval += 5
+ ticker.Reset(time.Duration(interval) * time.Second)
+ case errAuthorizationPending:
+ // Do nothing.
+ case errAccessDenied, errExpiredToken:
+ fallthrough
+ default:
+ return tok, err
+ }
+ }
+ }
+}
diff --git a/vendor/golang.org/x/oauth2/internal/client_appengine.go b/vendor/golang.org/x/oauth2/internal/client_appengine.go
deleted file mode 100644
index e1755d1..0000000
--- a/vendor/golang.org/x/oauth2/internal/client_appengine.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2018 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.
-
-//go:build appengine
-// +build appengine
-
-package internal
-
-import "google.golang.org/appengine/urlfetch"
-
-func init() {
- appengineClientHook = urlfetch.Client
-}
diff --git a/vendor/golang.org/x/oauth2/internal/doc.go b/vendor/golang.org/x/oauth2/internal/doc.go
index 03265e8..8c7c475 100644
--- a/vendor/golang.org/x/oauth2/internal/doc.go
+++ b/vendor/golang.org/x/oauth2/internal/doc.go
@@ -2,5 +2,5 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package internal contains support packages for oauth2 package.
+// Package internal contains support packages for [golang.org/x/oauth2].
package internal
diff --git a/vendor/golang.org/x/oauth2/internal/oauth2.go b/vendor/golang.org/x/oauth2/internal/oauth2.go
index c0ab196..71ea6ad 100644
--- a/vendor/golang.org/x/oauth2/internal/oauth2.go
+++ b/vendor/golang.org/x/oauth2/internal/oauth2.go
@@ -13,8 +13,8 @@
)
// ParseKey converts the binary contents of a private key file
-// to an *rsa.PrivateKey. It detects whether the private key is in a
-// PEM container or not. If so, it extracts the the private key
+// to an [*rsa.PrivateKey]. It detects whether the private key is in a
+// PEM container or not. If so, it extracts the private key
// from PEM container before conversion. It only supports PEM
// containers with no passphrase.
func ParseKey(key []byte) (*rsa.PrivateKey, error) {
diff --git a/vendor/golang.org/x/oauth2/internal/token.go b/vendor/golang.org/x/oauth2/internal/token.go
index b4723fc..8389f24 100644
--- a/vendor/golang.org/x/oauth2/internal/token.go
+++ b/vendor/golang.org/x/oauth2/internal/token.go
@@ -10,7 +10,6 @@
"errors"
"fmt"
"io"
- "io/ioutil"
"math"
"mime"
"net/http"
@@ -18,6 +17,7 @@
"strconv"
"strings"
"sync"
+ "sync/atomic"
"time"
)
@@ -25,9 +25,9 @@
// the requests to access protected resources on the OAuth 2.0
// provider's backend.
//
-// This type is a mirror of oauth2.Token and exists to break
+// This type is a mirror of [golang.org/x/oauth2.Token] and exists to break
// an otherwise-circular dependency. Other internal packages
-// should convert this Token into an oauth2.Token before use.
+// should convert this Token into an [golang.org/x/oauth2.Token] before use.
type Token struct {
// AccessToken is the token that authorizes and authenticates
// the requests.
@@ -49,18 +49,31 @@
// mechanisms for that TokenSource will not be used.
Expiry time.Time
+ // ExpiresIn is the OAuth2 wire format "expires_in" field,
+ // which specifies how many seconds later the token expires,
+ // relative to an unknown time base approximately around "now".
+ // It is the application's responsibility to populate
+ // `Expiry` from `ExpiresIn` when required.
+ ExpiresIn int64 `json:"expires_in,omitempty"`
+
// Raw optionally contains extra metadata from the server
// when updating a token.
- Raw interface{}
+ Raw any
}
// tokenJSON is the struct representing the HTTP response from OAuth2
-// providers returning a token in JSON form.
+// providers returning a token or error in JSON form.
+// https://datatracker.ietf.org/doc/html/rfc6749#section-5.1
type tokenJSON struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
RefreshToken string `json:"refresh_token"`
ExpiresIn expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
+ // error fields
+ // https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
+ ErrorCode string `json:"error"`
+ ErrorDescription string `json:"error_description"`
+ ErrorURI string `json:"error_uri"`
}
func (e *tokenJSON) expiry() (t time.Time) {
@@ -92,14 +105,6 @@
return nil
}
-// RegisterBrokenAuthHeaderProvider previously did something. It is now a no-op.
-//
-// Deprecated: this function no longer does anything. Caller code that
-// wants to avoid potential extra HTTP requests made during
-// auto-probing of the provider's auth style should set
-// Endpoint.AuthStyle.
-func RegisterBrokenAuthHeaderProvider(tokenURL string) {}
-
// AuthStyle is a copy of the golang.org/x/oauth2 package's AuthStyle type.
type AuthStyle int
@@ -109,41 +114,65 @@
AuthStyleInHeader AuthStyle = 2
)
-// authStyleCache is the set of tokenURLs we've successfully used via
+// LazyAuthStyleCache is a backwards compatibility compromise to let Configs
+// have a lazily-initialized AuthStyleCache.
+//
+// The two users of this, oauth2.Config and oauth2/clientcredentials.Config,
+// both would ideally just embed an unexported AuthStyleCache but because both
+// were historically allowed to be copied by value we can't retroactively add an
+// uncopyable Mutex to them.
+//
+// We could use an atomic.Pointer, but that was added recently enough (in Go
+// 1.18) that we'd break Go 1.17 users where the tests as of 2023-08-03
+// still pass. By using an atomic.Value, it supports both Go 1.17 and
+// copying by value, even if that's not ideal.
+type LazyAuthStyleCache struct {
+ v atomic.Value // of *AuthStyleCache
+}
+
+func (lc *LazyAuthStyleCache) Get() *AuthStyleCache {
+ if c, ok := lc.v.Load().(*AuthStyleCache); ok {
+ return c
+ }
+ c := new(AuthStyleCache)
+ if !lc.v.CompareAndSwap(nil, c) {
+ c = lc.v.Load().(*AuthStyleCache)
+ }
+ return c
+}
+
+type authStyleCacheKey struct {
+ url string
+ clientID string
+}
+
+// AuthStyleCache is the set of tokenURLs we've successfully used via
// RetrieveToken and which style auth we ended up using.
// It's called a cache, but it doesn't (yet?) shrink. It's expected that
// the set of OAuth2 servers a program contacts over time is fixed and
// small.
-var authStyleCache struct {
- sync.Mutex
- m map[string]AuthStyle // keyed by tokenURL
-}
-
-// ResetAuthCache resets the global authentication style cache used
-// for AuthStyleUnknown token requests.
-func ResetAuthCache() {
- authStyleCache.Lock()
- defer authStyleCache.Unlock()
- authStyleCache.m = nil
+type AuthStyleCache struct {
+ mu sync.Mutex
+ m map[authStyleCacheKey]AuthStyle
}
// lookupAuthStyle reports which auth style we last used with tokenURL
// when calling RetrieveToken and whether we have ever done so.
-func lookupAuthStyle(tokenURL string) (style AuthStyle, ok bool) {
- authStyleCache.Lock()
- defer authStyleCache.Unlock()
- style, ok = authStyleCache.m[tokenURL]
+func (c *AuthStyleCache) lookupAuthStyle(tokenURL, clientID string) (style AuthStyle, ok bool) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ style, ok = c.m[authStyleCacheKey{tokenURL, clientID}]
return
}
// setAuthStyle adds an entry to authStyleCache, documented above.
-func setAuthStyle(tokenURL string, v AuthStyle) {
- authStyleCache.Lock()
- defer authStyleCache.Unlock()
- if authStyleCache.m == nil {
- authStyleCache.m = make(map[string]AuthStyle)
+func (c *AuthStyleCache) setAuthStyle(tokenURL, clientID string, v AuthStyle) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ if c.m == nil {
+ c.m = make(map[authStyleCacheKey]AuthStyle)
}
- authStyleCache.m[tokenURL] = v
+ c.m[authStyleCacheKey{tokenURL, clientID}] = v
}
// newTokenRequest returns a new *http.Request to retrieve a new token
@@ -183,10 +212,10 @@
return v2
}
-func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values, authStyle AuthStyle) (*Token, error) {
- needsAuthStyleProbe := authStyle == 0
+func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values, authStyle AuthStyle, styleCache *AuthStyleCache) (*Token, error) {
+ needsAuthStyleProbe := authStyle == AuthStyleUnknown
if needsAuthStyleProbe {
- if style, ok := lookupAuthStyle(tokenURL); ok {
+ if style, ok := styleCache.lookupAuthStyle(tokenURL, clientID); ok {
authStyle = style
needsAuthStyleProbe = false
} else {
@@ -216,7 +245,7 @@
token, err = doTokenRoundTrip(ctx, req)
}
if needsAuthStyleProbe && err == nil {
- setAuthStyle(tokenURL, authStyle)
+ styleCache.setAuthStyle(tokenURL, clientID, authStyle)
}
// Don't overwrite `RefreshToken` with an empty value
// if this was a token refreshing request.
@@ -231,26 +260,34 @@
if err != nil {
return nil, err
}
- body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
+ body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20))
r.Body.Close()
if err != nil {
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
}
- if code := r.StatusCode; code < 200 || code > 299 {
- return nil, &RetrieveError{
- Response: r,
- Body: body,
- }
+
+ failureStatus := r.StatusCode < 200 || r.StatusCode > 299
+ retrieveError := &RetrieveError{
+ Response: r,
+ Body: body,
+ // attempt to populate error detail below
}
var token *Token
content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
switch content {
case "application/x-www-form-urlencoded", "text/plain":
+ // some endpoints return a query string
vals, err := url.ParseQuery(string(body))
if err != nil {
- return nil, err
+ if failureStatus {
+ return nil, retrieveError
+ }
+ return nil, fmt.Errorf("oauth2: cannot parse response: %v", err)
}
+ retrieveError.ErrorCode = vals.Get("error")
+ retrieveError.ErrorDescription = vals.Get("error_description")
+ retrieveError.ErrorURI = vals.Get("error_uri")
token = &Token{
AccessToken: vals.Get("access_token"),
TokenType: vals.Get("token_type"),
@@ -265,28 +302,55 @@
default:
var tj tokenJSON
if err = json.Unmarshal(body, &tj); err != nil {
- return nil, err
+ if failureStatus {
+ return nil, retrieveError
+ }
+ return nil, fmt.Errorf("oauth2: cannot parse json: %v", err)
}
+ retrieveError.ErrorCode = tj.ErrorCode
+ retrieveError.ErrorDescription = tj.ErrorDescription
+ retrieveError.ErrorURI = tj.ErrorURI
token = &Token{
AccessToken: tj.AccessToken,
TokenType: tj.TokenType,
RefreshToken: tj.RefreshToken,
Expiry: tj.expiry(),
- Raw: make(map[string]interface{}),
+ ExpiresIn: int64(tj.ExpiresIn),
+ Raw: make(map[string]any),
}
json.Unmarshal(body, &token.Raw) // no error checks for optional fields
}
+ // according to spec, servers should respond status 400 in error case
+ // https://www.rfc-editor.org/rfc/rfc6749#section-5.2
+ // but some unorthodox servers respond 200 in error case
+ if failureStatus || retrieveError.ErrorCode != "" {
+ return nil, retrieveError
+ }
if token.AccessToken == "" {
return nil, errors.New("oauth2: server response missing access_token")
}
return token, nil
}
+// mirrors oauth2.RetrieveError
type RetrieveError struct {
- Response *http.Response
- Body []byte
+ Response *http.Response
+ Body []byte
+ ErrorCode string
+ ErrorDescription string
+ ErrorURI string
}
func (r *RetrieveError) Error() string {
+ if r.ErrorCode != "" {
+ s := fmt.Sprintf("oauth2: %q", r.ErrorCode)
+ if r.ErrorDescription != "" {
+ s += fmt.Sprintf(" %q", r.ErrorDescription)
+ }
+ if r.ErrorURI != "" {
+ s += fmt.Sprintf(" %q", r.ErrorURI)
+ }
+ return s
+ }
return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
}
diff --git a/vendor/golang.org/x/oauth2/internal/transport.go b/vendor/golang.org/x/oauth2/internal/transport.go
index 572074a..afc0aeb 100644
--- a/vendor/golang.org/x/oauth2/internal/transport.go
+++ b/vendor/golang.org/x/oauth2/internal/transport.go
@@ -9,8 +9,8 @@
"net/http"
)
-// HTTPClient is the context key to use with golang.org/x/net/context's
-// WithValue function to associate an *http.Client value with a context.
+// HTTPClient is the context key to use with [context.WithValue]
+// to associate an [*http.Client] value with a context.
var HTTPClient ContextKey
// ContextKey is just an empty struct. It exists so HTTPClient can be
@@ -18,16 +18,11 @@
// because nobody else can create a ContextKey, being unexported.
type ContextKey struct{}
-var appengineClientHook func(context.Context) *http.Client
-
func ContextClient(ctx context.Context) *http.Client {
if ctx != nil {
if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok {
return hc
}
}
- if appengineClientHook != nil {
- return appengineClientHook(ctx)
- }
return http.DefaultClient
}
diff --git a/vendor/golang.org/x/oauth2/oauth2.go b/vendor/golang.org/x/oauth2/oauth2.go
index 291df5c..3e3b630 100644
--- a/vendor/golang.org/x/oauth2/oauth2.go
+++ b/vendor/golang.org/x/oauth2/oauth2.go
@@ -9,21 +9,21 @@
package oauth2 // import "golang.org/x/oauth2"
import (
- "bytes"
"context"
"errors"
"net/http"
"net/url"
"strings"
"sync"
+ "time"
"golang.org/x/oauth2/internal"
)
// NoContext is the default context you should supply if not using
-// your own context.Context (see https://golang.org/x/net/context).
+// your own [context.Context].
//
-// Deprecated: Use context.Background() or context.TODO() instead.
+// Deprecated: Use [context.Background] or [context.TODO] instead.
var NoContext = context.TODO()
// RegisterBrokenAuthHeaderProvider previously did something. It is now a no-op.
@@ -36,8 +36,8 @@
// Config describes a typical 3-legged OAuth2 flow, with both the
// client application information and the server's endpoint URLs.
-// For the client credentials 2-legged OAuth2 flow, see the clientcredentials
-// package (https://golang.org/x/oauth2/clientcredentials).
+// For the client credentials 2-legged OAuth2 flow, see the
+// [golang.org/x/oauth2/clientcredentials] package.
type Config struct {
// ClientID is the application's ID.
ClientID string
@@ -45,7 +45,7 @@
// ClientSecret is the application's secret.
ClientSecret string
- // Endpoint contains the resource server's token endpoint
+ // Endpoint contains the authorization server's token endpoint
// URLs. These are constants specific to each server and are
// often available via site-specific packages, such as
// google.Endpoint or github.Endpoint.
@@ -55,8 +55,12 @@
// the OAuth flow, after the resource owner's URLs.
RedirectURL string
- // Scope specifies optional requested permissions.
+ // Scopes specifies optional requested permissions.
Scopes []string
+
+ // authStyleCache caches which auth style to use when Endpoint.AuthStyle is
+ // the zero value (AuthStyleAutoDetect).
+ authStyleCache internal.LazyAuthStyleCache
}
// A TokenSource is anything that can return a token.
@@ -70,8 +74,9 @@
// Endpoint represents an OAuth 2.0 provider's authorization and token
// endpoint URLs.
type Endpoint struct {
- AuthURL string
- TokenURL string
+ AuthURL string
+ DeviceAuthURL string
+ TokenURL string
// AuthStyle optionally specifies how the endpoint wants the
// client ID & client secret sent. The zero value means to
@@ -129,7 +134,7 @@
func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) }
-// SetAuthURLParam builds an AuthCodeOption which passes key/value parameters
+// SetAuthURLParam builds an [AuthCodeOption] which passes key/value parameters
// to a provider's authorization endpoint.
func SetAuthURLParam(key, value string) AuthCodeOption {
return setParam{key, value}
@@ -138,17 +143,21 @@
// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
// that asks for permissions for the required scopes explicitly.
//
-// State is a token to protect the user from CSRF attacks. You must
-// always provide a non-empty string and validate that it matches the
-// the state query parameter on your redirect callback.
-// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
+// State is an opaque value used by the client to maintain state between the
+// request and callback. The authorization server includes this value when
+// redirecting the user agent back to the client.
//
-// Opts may include AccessTypeOnline or AccessTypeOffline, as well
-// as ApprovalForce.
-// It can also be used to pass the PKCE challenge.
-// See https://www.oauth.com/oauth2-servers/pkce/ for more info.
+// Opts may include [AccessTypeOnline] or [AccessTypeOffline], as well
+// as [ApprovalForce].
+//
+// To protect against CSRF attacks, opts should include a PKCE challenge
+// (S256ChallengeOption). Not all servers support PKCE. An alternative is to
+// generate a random state parameter and verify it after exchange.
+// See https://datatracker.ietf.org/doc/html/rfc6749#section-10.12 (predating
+// PKCE), https://www.oauth.com/oauth2-servers/pkce/ and
+// https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-09.html#name-cross-site-request-forgery (describing both approaches)
func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
- var buf bytes.Buffer
+ var buf strings.Builder
buf.WriteString(c.Endpoint.AuthURL)
v := url.Values{
"response_type": {"code"},
@@ -161,7 +170,6 @@
v.Set("scope", strings.Join(c.Scopes, " "))
}
if state != "" {
- // TODO(light): Docs say never to omit state; don't allow empty.
v.Set("state", state)
}
for _, opt := range opts {
@@ -185,7 +193,7 @@
// and when other authorization grant types are not available."
// See https://tools.ietf.org/html/rfc6749#section-4.3 for more info.
//
-// The provided context optionally controls which HTTP client is used. See the HTTPClient variable.
+// The provided context optionally controls which HTTP client is used. See the [HTTPClient] variable.
func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) {
v := url.Values{
"grant_type": {"password"},
@@ -203,13 +211,14 @@
// It is used after a resource provider redirects the user back
// to the Redirect URI (the URL obtained from AuthCodeURL).
//
-// The provided context optionally controls which HTTP client is used. See the HTTPClient variable.
+// The provided context optionally controls which HTTP client is used. See the [HTTPClient] variable.
//
-// The code will be in the *http.Request.FormValue("code"). Before
-// calling Exchange, be sure to validate FormValue("state").
+// The code will be in the [http.Request.FormValue]("code"). Before
+// calling Exchange, be sure to validate [http.Request.FormValue]("state") if you are
+// using it to protect against CSRF attacks.
//
-// Opts may include the PKCE verifier code if previously used in AuthCodeURL.
-// See https://www.oauth.com/oauth2-servers/pkce/ for more info.
+// If using PKCE to protect against CSRF attacks, opts should include a
+// VerifierOption.
func (c *Config) Exchange(ctx context.Context, code string, opts ...AuthCodeOption) (*Token, error) {
v := url.Values{
"grant_type": {"authorization_code"},
@@ -232,10 +241,10 @@
return NewClient(ctx, c.TokenSource(ctx, t))
}
-// TokenSource returns a TokenSource that returns t until t expires,
+// TokenSource returns a [TokenSource] that returns t until t expires,
// automatically refreshing it as necessary using the provided context.
//
-// Most users will use Config.Client instead.
+// Most users will use [Config.Client] instead.
func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource {
tkr := &tokenRefresher{
ctx: ctx,
@@ -250,7 +259,7 @@
}
}
-// tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token"
+// tokenRefresher is a TokenSource that makes "grant_type=refresh_token"
// HTTP requests to renew a token using a RefreshToken.
type tokenRefresher struct {
ctx context.Context // used to get HTTP requests
@@ -278,7 +287,7 @@
if tf.refreshToken != tk.RefreshToken {
tf.refreshToken = tk.RefreshToken
}
- return tk, err
+ return tk, nil
}
// reuseTokenSource is a TokenSource that holds a single token in memory
@@ -290,11 +299,12 @@
mu sync.Mutex // guards t
t *Token
+
+ expiryDelta time.Duration
}
// Token returns the current token if it's still valid, else will
-// refresh the current token (using r.Context for HTTP client
-// information) and return the new one.
+// refresh the current token and return the new one.
func (s *reuseTokenSource) Token() (*Token, error) {
s.mu.Lock()
defer s.mu.Unlock()
@@ -305,11 +315,12 @@
if err != nil {
return nil, err
}
+ t.expiryDelta = s.expiryDelta
s.t = t
return t, nil
}
-// StaticTokenSource returns a TokenSource that always returns the same token.
+// StaticTokenSource returns a [TokenSource] that always returns the same token.
// Because the provided token t is never refreshed, StaticTokenSource is only
// useful for tokens that never expire.
func StaticTokenSource(t *Token) TokenSource {
@@ -325,16 +336,16 @@
return s.t, nil
}
-// HTTPClient is the context key to use with golang.org/x/net/context's
-// WithValue function to associate an *http.Client value with a context.
+// HTTPClient is the context key to use with [context.WithValue]
+// to associate a [*http.Client] value with a context.
var HTTPClient internal.ContextKey
-// NewClient creates an *http.Client from a Context and TokenSource.
+// NewClient creates an [*http.Client] from a [context.Context] and [TokenSource].
// The returned client is not valid beyond the lifetime of the context.
//
-// Note that if a custom *http.Client is provided via the Context it
+// Note that if a custom [*http.Client] is provided via the [context.Context] it
// is used only for token acquisition and is not used to configure the
-// *http.Client returned from NewClient.
+// [*http.Client] returned from NewClient.
//
// As a special case, if src is nil, a non-OAuth2 client is returned
// using the provided context. This exists to support related OAuth2
@@ -343,15 +354,19 @@
if src == nil {
return internal.ContextClient(ctx)
}
+ cc := internal.ContextClient(ctx)
return &http.Client{
Transport: &Transport{
- Base: internal.ContextClient(ctx).Transport,
+ Base: cc.Transport,
Source: ReuseTokenSource(nil, src),
},
+ CheckRedirect: cc.CheckRedirect,
+ Jar: cc.Jar,
+ Timeout: cc.Timeout,
}
}
-// ReuseTokenSource returns a TokenSource which repeatedly returns the
+// ReuseTokenSource returns a [TokenSource] which repeatedly returns the
// same token as long as it's valid, starting with t.
// When its cached token is invalid, a new token is obtained from src.
//
@@ -359,10 +374,10 @@
// (such as a file on disk) between runs of a program, rather than
// obtaining new tokens unnecessarily.
//
-// The initial token t may be nil, in which case the TokenSource is
+// The initial token t may be nil, in which case the [TokenSource] is
// wrapped in a caching version if it isn't one already. This also
// means it's always safe to wrap ReuseTokenSource around any other
-// TokenSource without adverse effects.
+// [TokenSource] without adverse effects.
func ReuseTokenSource(t *Token, src TokenSource) TokenSource {
// Don't wrap a reuseTokenSource in itself. That would work,
// but cause an unnecessary number of mutex operations.
@@ -379,3 +394,30 @@
new: src,
}
}
+
+// ReuseTokenSourceWithExpiry returns a [TokenSource] that acts in the same manner as the
+// [TokenSource] returned by [ReuseTokenSource], except the expiry buffer is
+// configurable. The expiration time of a token is calculated as
+// t.Expiry.Add(-earlyExpiry).
+func ReuseTokenSourceWithExpiry(t *Token, src TokenSource, earlyExpiry time.Duration) TokenSource {
+ // Don't wrap a reuseTokenSource in itself. That would work,
+ // but cause an unnecessary number of mutex operations.
+ // Just build the equivalent one.
+ if rt, ok := src.(*reuseTokenSource); ok {
+ if t == nil {
+ // Just use it directly, but set the expiryDelta to earlyExpiry,
+ // so the behavior matches what the user expects.
+ rt.expiryDelta = earlyExpiry
+ return rt
+ }
+ src = rt.new
+ }
+ if t != nil {
+ t.expiryDelta = earlyExpiry
+ }
+ return &reuseTokenSource{
+ t: t,
+ new: src,
+ expiryDelta: earlyExpiry,
+ }
+}
diff --git a/vendor/golang.org/x/oauth2/pkce.go b/vendor/golang.org/x/oauth2/pkce.go
new file mode 100644
index 0000000..cea8374
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/pkce.go
@@ -0,0 +1,69 @@
+// Copyright 2023 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 oauth2
+
+import (
+ "crypto/rand"
+ "crypto/sha256"
+ "encoding/base64"
+ "net/url"
+)
+
+const (
+ codeChallengeKey = "code_challenge"
+ codeChallengeMethodKey = "code_challenge_method"
+ codeVerifierKey = "code_verifier"
+)
+
+// GenerateVerifier generates a PKCE code verifier with 32 octets of randomness.
+// This follows recommendations in RFC 7636.
+//
+// A fresh verifier should be generated for each authorization.
+// The resulting verifier should be passed to [Config.AuthCodeURL] or [Config.DeviceAuth]
+// with [S256ChallengeOption], and to [Config.Exchange] or [Config.DeviceAccessToken]
+// with [VerifierOption].
+func GenerateVerifier() string {
+ // "RECOMMENDED that the output of a suitable random number generator be
+ // used to create a 32-octet sequence. The octet sequence is then
+ // base64url-encoded to produce a 43-octet URL-safe string to use as the
+ // code verifier."
+ // https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
+ data := make([]byte, 32)
+ if _, err := rand.Read(data); err != nil {
+ panic(err)
+ }
+ return base64.RawURLEncoding.EncodeToString(data)
+}
+
+// VerifierOption returns a PKCE code verifier [AuthCodeOption]. It should only be
+// passed to [Config.Exchange] or [Config.DeviceAccessToken].
+func VerifierOption(verifier string) AuthCodeOption {
+ return setParam{k: codeVerifierKey, v: verifier}
+}
+
+// S256ChallengeFromVerifier returns a PKCE code challenge derived from verifier with method S256.
+//
+// Prefer to use [S256ChallengeOption] where possible.
+func S256ChallengeFromVerifier(verifier string) string {
+ sha := sha256.Sum256([]byte(verifier))
+ return base64.RawURLEncoding.EncodeToString(sha[:])
+}
+
+// S256ChallengeOption derives a PKCE code challenge derived from verifier with
+// method S256. It should be passed to [Config.AuthCodeURL] or [Config.DeviceAuth]
+// only.
+func S256ChallengeOption(verifier string) AuthCodeOption {
+ return challengeOption{
+ challenge_method: "S256",
+ challenge: S256ChallengeFromVerifier(verifier),
+ }
+}
+
+type challengeOption struct{ challenge_method, challenge string }
+
+func (p challengeOption) setValue(m url.Values) {
+ m.Set(codeChallengeMethodKey, p.challenge_method)
+ m.Set(codeChallengeKey, p.challenge)
+}
diff --git a/vendor/golang.org/x/oauth2/token.go b/vendor/golang.org/x/oauth2/token.go
index 8227203..239ec32 100644
--- a/vendor/golang.org/x/oauth2/token.go
+++ b/vendor/golang.org/x/oauth2/token.go
@@ -16,10 +16,10 @@
"golang.org/x/oauth2/internal"
)
-// expiryDelta determines how earlier a token should be considered
+// defaultExpiryDelta determines how earlier a token should be considered
// expired than its actual expiration time. It is used to avoid late
// expirations due to client-server time mismatches.
-const expiryDelta = 10 * time.Second
+const defaultExpiryDelta = 10 * time.Second
// Token represents the credentials used to authorize
// the requests to access protected resources on the OAuth 2.0
@@ -44,14 +44,26 @@
// Expiry is the optional expiration time of the access token.
//
- // If zero, TokenSource implementations will reuse the same
+ // If zero, [TokenSource] implementations will reuse the same
// token forever and RefreshToken or equivalent
// mechanisms for that TokenSource will not be used.
Expiry time.Time `json:"expiry,omitempty"`
+ // ExpiresIn is the OAuth2 wire format "expires_in" field,
+ // which specifies how many seconds later the token expires,
+ // relative to an unknown time base approximately around "now".
+ // It is the application's responsibility to populate
+ // `Expiry` from `ExpiresIn` when required.
+ ExpiresIn int64 `json:"expires_in,omitempty"`
+
// raw optionally contains extra metadata from the server
// when updating a token.
- raw interface{}
+ raw any
+
+ // expiryDelta is used to calculate when a token is considered
+ // expired, by subtracting from Expiry. If zero, defaultExpiryDelta
+ // is used.
+ expiryDelta time.Duration
}
// Type returns t.TokenType if non-empty, else "Bearer".
@@ -74,16 +86,16 @@
// SetAuthHeader sets the Authorization header to r using the access
// token in t.
//
-// This method is unnecessary when using Transport or an HTTP Client
+// This method is unnecessary when using [Transport] or an HTTP Client
// returned by this package.
func (t *Token) SetAuthHeader(r *http.Request) {
r.Header.Set("Authorization", t.Type()+" "+t.AccessToken)
}
-// WithExtra returns a new Token that's a clone of t, but using the
+// WithExtra returns a new [Token] that's a clone of t, but using the
// provided raw extra map. This is only intended for use by packages
// implementing derivative OAuth2 flows.
-func (t *Token) WithExtra(extra interface{}) *Token {
+func (t *Token) WithExtra(extra any) *Token {
t2 := new(Token)
*t2 = *t
t2.raw = extra
@@ -93,8 +105,8 @@
// Extra returns an extra field.
// Extra fields are key-value pairs returned by the server as a
// part of the token retrieval response.
-func (t *Token) Extra(key string) interface{} {
- if raw, ok := t.raw.(map[string]interface{}); ok {
+func (t *Token) Extra(key string) any {
+ if raw, ok := t.raw.(map[string]any); ok {
return raw[key]
}
@@ -127,6 +139,11 @@
if t.Expiry.IsZero() {
return false
}
+
+ expiryDelta := defaultExpiryDelta
+ if t.expiryDelta != 0 {
+ expiryDelta = t.expiryDelta
+ }
return t.Expiry.Round(0).Add(-expiryDelta).Before(timeNow())
}
@@ -146,15 +163,16 @@
TokenType: t.TokenType,
RefreshToken: t.RefreshToken,
Expiry: t.Expiry,
+ ExpiresIn: t.ExpiresIn,
raw: t.Raw,
}
}
// retrieveToken takes a *Config and uses that to retrieve an *internal.Token.
// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along
-// with an error..
+// with an error.
func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
- tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v, internal.AuthStyle(c.Endpoint.AuthStyle))
+ tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v, internal.AuthStyle(c.Endpoint.AuthStyle), c.authStyleCache.Get())
if err != nil {
if rErr, ok := err.(*internal.RetrieveError); ok {
return nil, (*RetrieveError)(rErr)
@@ -165,14 +183,31 @@
}
// RetrieveError is the error returned when the token endpoint returns a
-// non-2XX HTTP status code.
+// non-2XX HTTP status code or populates RFC 6749's 'error' parameter.
+// https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
type RetrieveError struct {
Response *http.Response
// Body is the body that was consumed by reading Response.Body.
// It may be truncated.
Body []byte
+ // ErrorCode is RFC 6749's 'error' parameter.
+ ErrorCode string
+ // ErrorDescription is RFC 6749's 'error_description' parameter.
+ ErrorDescription string
+ // ErrorURI is RFC 6749's 'error_uri' parameter.
+ ErrorURI string
}
func (r *RetrieveError) Error() string {
+ if r.ErrorCode != "" {
+ s := fmt.Sprintf("oauth2: %q", r.ErrorCode)
+ if r.ErrorDescription != "" {
+ s += fmt.Sprintf(" %q", r.ErrorDescription)
+ }
+ if r.ErrorURI != "" {
+ s += fmt.Sprintf(" %q", r.ErrorURI)
+ }
+ return s
+ }
return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
}
diff --git a/vendor/golang.org/x/oauth2/transport.go b/vendor/golang.org/x/oauth2/transport.go
index 9065791..8bbebba 100644
--- a/vendor/golang.org/x/oauth2/transport.go
+++ b/vendor/golang.org/x/oauth2/transport.go
@@ -11,12 +11,12 @@
"sync"
)
-// Transport is an http.RoundTripper that makes OAuth 2.0 HTTP requests,
-// wrapping a base RoundTripper and adding an Authorization header
-// with a token from the supplied Sources.
+// Transport is an [http.RoundTripper] that makes OAuth 2.0 HTTP requests,
+// wrapping a base [http.RoundTripper] and adding an Authorization header
+// with a token from the supplied [TokenSource].
//
// Transport is a low-level mechanism. Most code will use the
-// higher-level Config.Client method instead.
+// higher-level [Config.Client] method instead.
type Transport struct {
// Source supplies the token to add to outgoing requests'
// Authorization headers.
@@ -47,7 +47,7 @@
return nil, err
}
- req2 := cloneRequest(req) // per RoundTripper contract
+ req2 := req.Clone(req.Context())
token.SetAuthHeader(req2)
// req.Body is assumed to be closed by the base RoundTripper.
@@ -73,17 +73,3 @@
}
return http.DefaultTransport
}
-
-// cloneRequest returns a clone of the provided *http.Request.
-// The clone is a shallow copy of the struct and its Header map.
-func cloneRequest(r *http.Request) *http.Request {
- // shallow copy of the struct
- r2 := new(http.Request)
- *r2 = *r
- // deep copy of the Header
- r2.Header = make(http.Header, len(r.Header))
- for k, s := range r.Header {
- r2.Header[k] = append([]string(nil), s...)
- }
- return r2
-}