diff --git a/pkg/pmapi/auth_test.go b/pkg/pmapi/auth_test.go index 5ccb254b..c946f7df 100644 --- a/pkg/pmapi/auth_test.go +++ b/pkg/pmapi/auth_test.go @@ -143,6 +143,51 @@ func Test401RevokedAuth(t *testing.T) { r.EqualError(t, err, ErrUnauthorized.Error()) } +func Test401RevokedAuthTokenUpdate(t *testing.T) { + var oldAuth = &AuthRefresh{ + UID: "UID", + AccessToken: "oldAcc", + RefreshToken: "oldRef", + ExpiresIn: 3600, + } + + var newAuth = &AuthRefresh{ + UID: "UID", + AccessToken: "newAcc", + RefreshToken: "newRef", + } + + mux := http.NewServeMux() + + mux.HandleFunc("/auth/refresh", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(newAuth); err != nil { + panic(err) + } + }) + + mux.HandleFunc("/addresses", func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Authorization") == ("Bearer " + oldAuth.AccessToken) { + w.WriteHeader(http.StatusUnauthorized) + return + } + + if r.Header.Get("Authorization") == ("Bearer " + newAuth.AccessToken) { + w.WriteHeader(http.StatusOK) + return + } + }) + + ts := httptest.NewServer(mux) + + c := New(Config{HostURL: ts.URL}). + NewClient(oldAuth.UID, oldAuth.AccessToken, oldAuth.RefreshToken, time.Now().Add(time.Hour)) + + // The request will fail with 401, triggering a refresh. After the refresh it should succeed. + _, err := c.GetAddresses(context.Background()) + r.NoError(t, err) +} + func TestAuth2FA(t *testing.T) { twoFACode := "code" diff --git a/pkg/pmapi/client.go b/pkg/pmapi/client.go index edbae830..4826f130 100644 --- a/pkg/pmapi/client.go +++ b/pkg/pmapi/client.go @@ -84,6 +84,8 @@ func (c *client) r(ctx context.Context) (*resty.Request, error) { return r, nil } +// do executes fn and may repeate it in case "401 Unauthorized" error is returned. +// Note: fn may be called more than once. func (c *client) do(ctx context.Context, fn func(*resty.Request) (*resty.Response, error)) (*resty.Response, error) { r, err := c.r(ctx) if err != nil { @@ -102,6 +104,12 @@ func (c *client) do(ctx context.Context, fn func(*resty.Request) (*resty.Respons return nil, err } + // We need to reconstruct request since access token is changed with authRefresh. + r, err := c.r(ctx) + if err != nil { + return nil, err + } + return wrapNoConnection(fn(r)) }