From 55dceeaa4eae243fee18eeb7f1f74050b7335227 Mon Sep 17 00:00:00 2001 From: Mark Beamer Jr Date: Sun, 8 Aug 2021 14:08:47 -0400 Subject: [PATCH] Add OAuth client for internal-apis to be used for odysee-apis --- extras/lbryinc/client.go | 40 ++++++++++++++++++++++++++++++++++- extras/lbryinc/client_test.go | 21 ++++++++++++++++++ go.mod | 3 ++- go.sum | 2 ++ 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/extras/lbryinc/client.go b/extras/lbryinc/client.go index e9f12d2..5b56925 100644 --- a/extras/lbryinc/client.go +++ b/extras/lbryinc/client.go @@ -10,6 +10,8 @@ import ( "net/url" "time" + "golang.org/x/oauth2" + log "github.com/sirupsen/logrus" ) @@ -26,6 +28,7 @@ const ( // Client stores data about internal-apis call it is about to make. type Client struct { AuthToken string + OAuthToken oauth2.TokenSource Logger *log.Logger serverAddress string extraHeaders map[string]string @@ -83,13 +86,38 @@ func NewClient(authToken string, opts *ClientOpts) Client { return c } +// NewOauthClient returns a client instance for internal-apis. It requires Oauth Token Source to be provided +// for authentication. +func NewOauthClient(token oauth2.TokenSource, opts *ClientOpts) Client { + c := Client{ + serverAddress: defaultServerAddress, + extraHeaders: make(map[string]string), + OAuthToken: token, + Logger: log.StandardLogger(), + } + if opts != nil { + if opts.ServerAddress != "" { + c.serverAddress = opts.ServerAddress + } + if opts.RemoteIP != "" { + c.extraHeaders[headerForwardedFor] = opts.RemoteIP + } + } + + return c +} + func (c Client) getEndpointURL(object, method string) string { return fmt.Sprintf("%s%s", c.serverAddress, makeMethodPath(object, method)) } func (c Client) prepareParams(params map[string]interface{}) (string, error) { form := url.Values{} - form.Add("auth_token", c.AuthToken) + if c.AuthToken != "" { + form.Add("auth_token", c.AuthToken) + } else if c.OAuthToken == nil { + return "", errors.New("oauth token source must be supplied") + } for k, v := range params { if k == "auth_token" { return "", errors.New("extra auth_token supplied in request params") @@ -109,6 +137,16 @@ func (c Client) doCall(url string, payload string) ([]byte, error) { req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + if c.OAuthToken != nil { + t, err := c.OAuthToken.Token() + if err != nil { + return nil, err + } + if t.Type() != "Bearer" { + return nil, errors.New("internal-apis requires an oAuth token of type 'Bearer'") + } + t.SetAuthHeader(req) + } for k, v := range c.extraHeaders { req.Header.Set(k, v) diff --git a/extras/lbryinc/client_test.go b/extras/lbryinc/client_test.go index 3b2714e..7fa4c58 100644 --- a/extras/lbryinc/client_test.go +++ b/extras/lbryinc/client_test.go @@ -6,6 +6,8 @@ import ( "net/http/httptest" "testing" + "golang.org/x/oauth2" + "github.com/stretchr/testify/assert" ) @@ -14,6 +16,14 @@ func launchDummyServer(lastReq **http.Request, path, response string, status int if lastReq != nil { *lastReq = &*r } + authT := r.FormValue("auth_token") + if authT == "" { + accessT := r.Header.Get("Authorization") + if accessT == "" { + w.WriteHeader(http.StatusUnauthorized) + return + } + } if r.URL.Path != path { fmt.Printf("path doesn't match: %v != %v", r.URL.Path, path) w.WriteHeader(http.StatusNotFound) @@ -46,6 +56,17 @@ func TestUserHasVerifiedEmail(t *testing.T) { assert.Equal(t, true, r["has_verified_email"]) } +func TestUserHasVerifiedEmailOAuth(t *testing.T) { + ts := launchDummyServer(nil, makeMethodPath(userObjectPath, userHasVerifiedEmailMethod), userHasVerifiedEmailResponse, http.StatusOK) + defer ts.Close() + + c := NewOauthClient(oauth2.StaticTokenSource(&oauth2.Token{AccessToken: "Test-Access-Token"}), &ClientOpts{ServerAddress: ts.URL}) + r, err := c.UserHasVerifiedEmail() + assert.Nil(t, err) + assert.EqualValues(t, 12345, r["user_id"]) + assert.Equal(t, true, r["has_verified_email"]) +} + func TestRemoteIP(t *testing.T) { var req *http.Request ts := launchDummyServer(&req, makeMethodPath(userObjectPath, userMeMethod), userMeResponse, http.StatusOK) diff --git a/go.mod b/go.mod index 09bb436..3e4be81 100644 --- a/go.mod +++ b/go.mod @@ -39,8 +39,9 @@ require ( go.uber.org/atomic v1.4.0 golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc golang.org/x/net v0.0.0-20191009170851-d66e71096ffb + golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be golang.org/x/sys v0.0.0-20191009170203-06d7bd2c5f4f // indirect - golang.org/x/text v0.3.2 // indirect + golang.org/x/text v0.3.2 golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03 // indirect google.golang.org/grpc v1.24.0 diff --git a/go.sum b/go.sum index 7537de5..fc2b977 100644 --- a/go.sum +++ b/go.sum @@ -128,6 +128,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20191009170851-d66e71096ffb h1:TR699M2v0qoKTOHxeLgp6zPqaQNs74f01a/ob9W0qko= golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -151,6 +152,7 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03 h1:4HYDjxeNXAOTv3o1N2tjo8UUSlhQgAD52FVkwxnWgM8=