mirror of
https://github.com/cubixle/ebay.git
synced 2026-04-29 11:18:42 +01:00
add clientcredentials pkg for oauth2
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
// Package clientcredentials implements the eBay client credentials grant flow
|
||||
// to generate "Application access" tokens.
|
||||
// eBay doc: https://developer.ebay.com/api-docs/static/oauth-client-credentials-grant.html
|
||||
//
|
||||
// The only difference from the original golang.org/x/oauth2/clientcredentials is the token type
|
||||
// being forced to "Bearer". The eBay api /identity/v1/oauth2/token endpoint returns
|
||||
// "Application Access Token" as token type which is then reused:
|
||||
// https://github.com/golang/oauth2/blob/aaccbc9213b0974828f81aaac109d194880e3014/token.go#L68-L70
|
||||
package clientcredentials
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
// HTTPClient used to make requests.
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// Token uses client credentials to retrieve a token.
|
||||
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 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, " "))
|
||||
}
|
||||
req, err := http.NewRequest("POST", c.conf.TokenURL, strings.NewReader(v.Encode()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.SetBasicAuth(url.QueryEscape(c.conf.ClientID), url.QueryEscape(c.conf.ClientSecret))
|
||||
client := c.conf.HTTPClient
|
||||
if client == nil {
|
||||
client = http.DefaultClient
|
||||
}
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot fetch token: %v", err)
|
||||
}
|
||||
if code := r.StatusCode; code < 200 || code > 299 {
|
||||
return nil, fmt.Errorf("%s (%d)", req.URL, r.StatusCode)
|
||||
}
|
||||
token := struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
}{}
|
||||
if err = json.NewDecoder(bytes.NewReader(body)).Decode(&token); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var expiry time.Time
|
||||
if secs := token.ExpiresIn; secs > 0 {
|
||||
expiry = time.Now().Add(time.Duration(secs) * time.Second)
|
||||
}
|
||||
t := oauth2.Token{
|
||||
AccessToken: token.AccessToken,
|
||||
Expiry: expiry,
|
||||
TokenType: "Bearer",
|
||||
}
|
||||
return &t, nil
|
||||
}
|
||||
Reference in New Issue
Block a user