summaryrefslogtreecommitdiff
path: root/src/oauth/client.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/oauth/client.go')
-rw-r--r--src/oauth/client.go112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/oauth/client.go b/src/oauth/client.go
new file mode 100644
index 0000000..684cb20
--- /dev/null
+++ b/src/oauth/client.go
@@ -0,0 +1,112 @@
+// 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
+}