// The oauth package provides a Client that authenticates requests // automatically with OAuth1. // // [Cardmarket OAuth] and [RFC 5849] were used as reference. // // [Cardmarket OAuth]: https://apiv2.cardmarket.com/ws/documentation/API:Auth_OAuthHeader // [RFC 5849]: https://datatracker.ietf.org/doc/html/rfc5849#section-3.1 package oauth import ( "context" "fmt" "io" "log/slog" "net/http" "strings" ) const ( headerAuthorization = "Authorization" headerContentType = "Content-Type" ) type ConsumerToken struct { Token string Secret string } type AccessToken struct { Token string Secret string } type Tokens struct { Consumer ConsumerToken Access AccessToken } type Client struct { tokens Tokens } func NewClient(t Tokens) *Client { return &Client{tokens: t} } func (c *Client) GetWithContext( ctx context.Context, url string, ) (*http.Response, error) { return c.request(ctx, http.MethodGet, url, "", nil) } func (c *Client) PostWithContext( ctx context.Context, url, contentType string, body io.Reader, ) (*http.Response, error) { return c.request(ctx, http.MethodPost, url, contentType, body) } // request hydrates and fulfils a http.Request using information stored in the client func (c *Client) request( ctx context.Context, method string, url, contentType string, body io.Reader, ) (*http.Response, error) { req, err := http.NewRequestWithContext(ctx, method, url, body) if err != nil { return nil, err } if contentType != "" { req.Header.Set("Content-Type", contentType) } if err = c.authorize(req); err != nil { return nil, err } slog.DebugContext( ctx, "sending request", "method", req.Method, "url", req.URL.String(), "contentType", req.Header.Get(headerContentType), "authorization", strings.Join(req.Header.Values(headerAuthorization), ", "), ) return http.DefaultClient.Do(req) } func (c *Client) authorize(req *http.Request) error { baseURL := fmt.Sprintf( "%s://%s%s", strings.ToLower(req.URL.Scheme), strings.ToLower(req.URL.Host), req.URL.Path, ) ah := newAuthHeader( req.Method, baseURL, nil, c.tokens, ) req.Header.Set(headerAuthorization, ah.String()) return nil }