From 7eff0ecc4d066d625121ea110a735c486b6e7c7e Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Thu, 20 Jun 2024 15:16:33 -0700 Subject: [PATCH 1/8] Periodically fetch the token --- cmd/login/login.go | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/cmd/login/login.go b/cmd/login/login.go index 95163770..0add0538 100644 --- a/cmd/login/login.go +++ b/cmd/login/login.go @@ -1,8 +1,11 @@ package login import ( + "encoding/json" + "errors" "fmt" "strings" + "time" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -11,6 +14,7 @@ import ( "github.com/launchdarkly/ldcli/cmd/cliflags" "github.com/launchdarkly/ldcli/internal/analytics" "github.com/launchdarkly/ldcli/internal/config" + errs "github.com/launchdarkly/ldcli/internal/errors" "github.com/launchdarkly/ldcli/internal/login" "github.com/launchdarkly/ldcli/internal/output" ) @@ -79,9 +83,46 @@ func run(client login.Client) func(*cobra.Command, []string) error { deviceAuthorization.VerificationURI, ), ) - fmt.Fprintln(cmd.OutOrStdout(), b.String()) + ticker := time.NewTicker(2000 * time.Millisecond) + var attempts int + maxAttempts := 3 + var deviceAuthorizationToken login.DeviceAuthorizationToken + for { + if attempts >= maxAttempts { + return errors.New("request timed-out after too many attempts") + } + <-ticker.C + deviceAuthorizationToken, err = login.FetchToken( + client, + deviceAuthorization.DeviceCode, + viper.GetString(cliflags.BaseURIFlag), + ) + if err == nil { + break + } + var e struct { + Code string `json:"code"` + Message string `json:"message"` + } + err := json.Unmarshal([]byte(err.Error()), &e) + if err != nil { + return errs.NewErrorWrapped("error reading response", err) + } + switch e.Message { + case "authorization_pending": + attempts += 1 + case "access_denied": + return errs.NewError("Your request has been denied. Please try logging in again.") + case "expired_token": + return errs.NewError("Your request has expired. Please try logging in again.") + default: + return errs.NewErrorWrapped("We cannot complete your request", err) + } + } + fmt.Fprintf(cmd.OutOrStdout(), "Your token is %s\n", deviceAuthorizationToken.AccessToken) + return nil } } From f7201fecfb30e61b9f46b7722bceb4fd109d7829 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 24 Jun 2024 09:27:20 -0700 Subject: [PATCH 2/8] Can fetch token and show various responses --- cmd/login/login.go | 52 ++-------- internal/login/login.go | 61 ++++++++++-- internal/login/login_test.go | 188 +++++++++++++++++++++++++++++++---- 3 files changed, 230 insertions(+), 71 deletions(-) diff --git a/cmd/login/login.go b/cmd/login/login.go index 0add0538..314d18c0 100644 --- a/cmd/login/login.go +++ b/cmd/login/login.go @@ -1,11 +1,8 @@ package login import ( - "encoding/json" - "errors" "fmt" "strings" - "time" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -14,9 +11,7 @@ import ( "github.com/launchdarkly/ldcli/cmd/cliflags" "github.com/launchdarkly/ldcli/internal/analytics" "github.com/launchdarkly/ldcli/internal/config" - errs "github.com/launchdarkly/ldcli/internal/errors" "github.com/launchdarkly/ldcli/internal/login" - "github.com/launchdarkly/ldcli/internal/output" ) func NewLoginCmd( @@ -70,7 +65,7 @@ func run(client login.Client) func(*cobra.Command, []string) error { viper.GetString(cliflags.BaseURIFlag), ) if err != nil { - return output.NewCmdOutputError(err, viper.GetString(cliflags.OutputFlag)) + return err } var b strings.Builder @@ -85,42 +80,17 @@ func run(client login.Client) func(*cobra.Command, []string) error { ) fmt.Fprintln(cmd.OutOrStdout(), b.String()) - ticker := time.NewTicker(2000 * time.Millisecond) - var attempts int - maxAttempts := 3 - var deviceAuthorizationToken login.DeviceAuthorizationToken - for { - if attempts >= maxAttempts { - return errors.New("request timed-out after too many attempts") - } - <-ticker.C - deviceAuthorizationToken, err = login.FetchToken( - client, - deviceAuthorization.DeviceCode, - viper.GetString(cliflags.BaseURIFlag), - ) - if err == nil { - break - } - var e struct { - Code string `json:"code"` - Message string `json:"message"` - } - err := json.Unmarshal([]byte(err.Error()), &e) - if err != nil { - return errs.NewErrorWrapped("error reading response", err) - } - switch e.Message { - case "authorization_pending": - attempts += 1 - case "access_denied": - return errs.NewError("Your request has been denied. Please try logging in again.") - case "expired_token": - return errs.NewError("Your request has expired. Please try logging in again.") - default: - return errs.NewErrorWrapped("We cannot complete your request", err) - } + deviceAuthorizationToken, err := login.FetchToken( + client, + deviceAuthorization.DeviceCode, + viper.GetString(cliflags.BaseURIFlag), + login.TokenInterval, + login.MaxFetchTokenAttempts, + ) + if err != nil { + return err } + fmt.Fprintf(cmd.OutOrStdout(), "Your token is %s\n", deviceAuthorizationToken.AccessToken) return nil diff --git a/internal/login/login.go b/internal/login/login.go index 4b135411..8756d966 100644 --- a/internal/login/login.go +++ b/internal/login/login.go @@ -7,11 +7,16 @@ import ( "io" "net/http" "os" + "time" "github.com/launchdarkly/ldcli/internal/errors" ) -const ClientID = "e6506150369268abae3ed46152687201" +const ( + ClientID = "e6506150369268abae3ed46152687201" + MaxFetchTokenAttempts = 120 // 2 minutes + TokenInterval = 1 * time.Second +) type DeviceAuthorization struct { DeviceCode string `json:"deviceCode"` @@ -104,15 +109,55 @@ func FetchToken( client UnauthenticatedClient, deviceCode string, baseURI string, + interval time.Duration, + maxAttempts int, +) (DeviceAuthorizationToken, error) { + var attempts int + for { + if attempts >= maxAttempts { + return DeviceAuthorizationToken{}, errors.NewError("The request timed-out after too many attempts.") + } + deviceAuthorizationToken, err := fetchToken( + client, + deviceCode, + baseURI, + ) + if err == nil { + return deviceAuthorizationToken, nil + } + + var e struct { + Code string `json:"code"` + Message string `json:"message"` + } + err = json.Unmarshal([]byte(err.Error()), &e) + if err != nil { + return DeviceAuthorizationToken{}, errors.NewErrorWrapped("error reading response", err) + } + switch e.Code { + case "authorization_pending": + attempts += 1 + case "access_denied": + return DeviceAuthorizationToken{}, errors.NewError("Your request has been denied. Please try logging in again.") + case "expired_token": + return DeviceAuthorizationToken{}, errors.NewError("Your request has expired. Please try logging in again.") + default: + return DeviceAuthorizationToken{}, errors.NewErrorWrapped("We cannot complete your request.", err) + } + time.Sleep(interval) + } +} + +func fetchToken( + client UnauthenticatedClient, + deviceCode string, + baseURI string, ) (DeviceAuthorizationToken, error) { path := fmt.Sprintf("%s/internal/device-authorization/token", baseURI) - body := fmt.Sprintf( - `{ - "deviceCode": %q - }`, - deviceCode, - ) - res, err := client.MakeRequest("POST", path, []byte(body)) + body, _ := json.Marshal(map[string]string{ + "deviceCode": deviceCode, + }) + res, err := client.MakeRequest("POST", path, body) if err != nil { return DeviceAuthorizationToken{}, err } diff --git a/internal/login/login_test.go b/internal/login/login_test.go index a258453a..fcf9d279 100644 --- a/internal/login/login_test.go +++ b/internal/login/login_test.go @@ -1,8 +1,11 @@ package login_test import ( + "encoding/json" "testing" + "time" + "github.com/launchdarkly/ldcli/internal/errors" "github.com/launchdarkly/ldcli/internal/login" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -60,29 +63,170 @@ func TestFetchDeviceAuthorization(t *testing.T) { assert.Equal(t, expected, result) } +// func TestFetchToken(t *testing.T) { +// baseURI := "http://test.com" +// mockClient := mockClient{} +// mockClient.On( +// "MakeRequest", +// "POST", +// "http://test.com/internal/device-authorization/token", +// []byte(`{ +// "deviceCode": "test-device-code" +// }`), +// ).Return([]byte(`{ +// "accessToken": "test-access-token" +// }`), nil) +// expected := login.DeviceAuthorizationToken{ +// AccessToken: "test-access-token", +// } + +// result, err := login.FetchToken( +// &mockClient, +// "test-device-code", +// baseURI, +// ) + +// require.NoError(t, err) +// assert.Equal(t, expected, result) +// } + func TestFetchToken(t *testing.T) { - baseURI := "http://test.com" - mockClient := mockClient{} - mockClient.On( - "MakeRequest", - "POST", - "http://test.com/internal/device-authorization/token", - []byte(`{ - "deviceCode": "test-device-code" - }`), - ).Return([]byte(`{ - "accessToken": "test-access-token" - }`), nil) - expected := login.DeviceAuthorizationToken{ - AccessToken: "test-access-token", - } + t.Run("with a token response", func(t *testing.T) { + input, _ := json.Marshal(map[string]string{ + "deviceCode": "test-device-code", + }) + output, _ := json.Marshal(map[string]string{ + "accessToken": "test-access-token", + }) + mockClient := mockClient{} + mockClient.On( + "MakeRequest", + "POST", + "http://test.com/internal/device-authorization/token", + input, + ).Return(output, nil) - result, err := login.FetchToken( - &mockClient, - "test-device-code", - baseURI, - ) + result, err := login.FetchToken( + &mockClient, + "test-device-code", + "http://test.com", + 1*time.Microsecond, + 2, + ) - require.NoError(t, err) - assert.Equal(t, expected, result) + require.NoError(t, err) + assert.Equal(t, "test-access-token", result.AccessToken) + }) + + t.Run("with an authorization pending response", func(t *testing.T) { + input, _ := json.Marshal(map[string]string{ + "deviceCode": "test-device-code", + }) + output, _ := json.Marshal(map[string]string{ + "code": "authorization_pending", + "message": "error message", + }) + responseErr := errors.NewError(string(output)) + mockClient := mockClient{} + mockClient.On( + "MakeRequest", + "POST", + "http://test.com/internal/device-authorization/token", + input, + ).Return([]byte(""), responseErr) + + _, err := login.FetchToken( + &mockClient, + "test-device-code", + "http://test.com", + 1*time.Microsecond, + 2, + ) + + assert.EqualError(t, err, "The request timed-out after too many attempts.") + }) + + t.Run("with an access denied response", func(t *testing.T) { + input, _ := json.Marshal(map[string]string{ + "deviceCode": "test-device-code", + }) + output, _ := json.Marshal(map[string]string{ + "code": "access_denied", + "message": "error message", + }) + responseErr := errors.NewError(string(output)) + mockClient := mockClient{} + mockClient.On( + "MakeRequest", + "POST", + "http://test.com/internal/device-authorization/token", + input, + ).Return([]byte(""), responseErr) + + _, err := login.FetchToken( + &mockClient, + "test-device-code", + "http://test.com", + 1*time.Microsecond, + 2, + ) + + assert.EqualError(t, err, "Your request has been denied. Please try logging in again.") + }) + + t.Run("with an expired token response", func(t *testing.T) { + input, _ := json.Marshal(map[string]string{ + "deviceCode": "test-device-code", + }) + output, _ := json.Marshal(map[string]string{ + "code": "expired_token", + "message": "error message", + }) + responseErr := errors.NewError(string(output)) + mockClient := mockClient{} + mockClient.On( + "MakeRequest", + "POST", + "http://test.com/internal/device-authorization/token", + input, + ).Return([]byte(""), responseErr) + + _, err := login.FetchToken( + &mockClient, + "test-device-code", + "http://test.com", + 1*time.Microsecond, + 2, + ) + + assert.EqualError(t, err, "Your request has expired. Please try logging in again.") + }) + + t.Run("with an error response", func(t *testing.T) { + input, _ := json.Marshal(map[string]string{ + "deviceCode": "test-device-code", + }) + output, _ := json.Marshal(map[string]string{ + "code": "error_code", + "message": "error message", + }) + responseErr := errors.NewError(string(output)) + mockClient := mockClient{} + mockClient.On( + "MakeRequest", + "POST", + "http://test.com/internal/device-authorization/token", + input, + ).Return([]byte(""), responseErr) + + _, err := login.FetchToken( + &mockClient, + "test-device-code", + "http://test.com", + 1*time.Microsecond, + 2, + ) + + assert.EqualError(t, err, "We cannot complete your request.") + }) } From bea539a17fe2c12678e4db96ab2e5e70e0fd49d4 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 24 Jun 2024 09:31:03 -0700 Subject: [PATCH 3/8] Remove commented-out code --- internal/login/login_test.go | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/internal/login/login_test.go b/internal/login/login_test.go index fcf9d279..5a483285 100644 --- a/internal/login/login_test.go +++ b/internal/login/login_test.go @@ -63,33 +63,6 @@ func TestFetchDeviceAuthorization(t *testing.T) { assert.Equal(t, expected, result) } -// func TestFetchToken(t *testing.T) { -// baseURI := "http://test.com" -// mockClient := mockClient{} -// mockClient.On( -// "MakeRequest", -// "POST", -// "http://test.com/internal/device-authorization/token", -// []byte(`{ -// "deviceCode": "test-device-code" -// }`), -// ).Return([]byte(`{ -// "accessToken": "test-access-token" -// }`), nil) -// expected := login.DeviceAuthorizationToken{ -// AccessToken: "test-access-token", -// } - -// result, err := login.FetchToken( -// &mockClient, -// "test-device-code", -// baseURI, -// ) - -// require.NoError(t, err) -// assert.Equal(t, expected, result) -// } - func TestFetchToken(t *testing.T) { t.Run("with a token response", func(t *testing.T) { input, _ := json.Marshal(map[string]string{ From c363b3a5901d59fc5bbfd1b45bd586717d84563b Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 24 Jun 2024 09:36:58 -0700 Subject: [PATCH 4/8] Refactor to table tests --- internal/login/login_test.go | 161 +++++++++++------------------------ 1 file changed, 51 insertions(+), 110 deletions(-) diff --git a/internal/login/login_test.go b/internal/login/login_test.go index 5a483285..403ee373 100644 --- a/internal/login/login_test.go +++ b/internal/login/login_test.go @@ -90,116 +90,57 @@ func TestFetchToken(t *testing.T) { require.NoError(t, err) assert.Equal(t, "test-access-token", result.AccessToken) }) +} - t.Run("with an authorization pending response", func(t *testing.T) { - input, _ := json.Marshal(map[string]string{ - "deviceCode": "test-device-code", - }) - output, _ := json.Marshal(map[string]string{ - "code": "authorization_pending", - "message": "error message", - }) - responseErr := errors.NewError(string(output)) - mockClient := mockClient{} - mockClient.On( - "MakeRequest", - "POST", - "http://test.com/internal/device-authorization/token", - input, - ).Return([]byte(""), responseErr) - - _, err := login.FetchToken( - &mockClient, - "test-device-code", - "http://test.com", - 1*time.Microsecond, - 2, - ) - - assert.EqualError(t, err, "The request timed-out after too many attempts.") - }) - - t.Run("with an access denied response", func(t *testing.T) { - input, _ := json.Marshal(map[string]string{ - "deviceCode": "test-device-code", - }) - output, _ := json.Marshal(map[string]string{ - "code": "access_denied", - "message": "error message", - }) - responseErr := errors.NewError(string(output)) - mockClient := mockClient{} - mockClient.On( - "MakeRequest", - "POST", - "http://test.com/internal/device-authorization/token", - input, - ).Return([]byte(""), responseErr) - - _, err := login.FetchToken( - &mockClient, - "test-device-code", - "http://test.com", - 1*time.Microsecond, - 2, - ) - - assert.EqualError(t, err, "Your request has been denied. Please try logging in again.") - }) - - t.Run("with an expired token response", func(t *testing.T) { - input, _ := json.Marshal(map[string]string{ - "deviceCode": "test-device-code", - }) - output, _ := json.Marshal(map[string]string{ - "code": "expired_token", - "message": "error message", - }) - responseErr := errors.NewError(string(output)) - mockClient := mockClient{} - mockClient.On( - "MakeRequest", - "POST", - "http://test.com/internal/device-authorization/token", - input, - ).Return([]byte(""), responseErr) - - _, err := login.FetchToken( - &mockClient, - "test-device-code", - "http://test.com", - 1*time.Microsecond, - 2, - ) - - assert.EqualError(t, err, "Your request has expired. Please try logging in again.") - }) - - t.Run("with an error response", func(t *testing.T) { - input, _ := json.Marshal(map[string]string{ - "deviceCode": "test-device-code", - }) - output, _ := json.Marshal(map[string]string{ - "code": "error_code", - "message": "error message", +func TestFetchToken_WithError(t *testing.T) { + tests := map[string]struct { + errCode string + expectedErr string + }{ + "with an authorization pending response": { + errCode: "authorization_pending", + expectedErr: "The request timed-out after too many attempts.", + }, + "with an access denied response": { + errCode: "access_denied", + expectedErr: "Your request has been denied. Please try logging in again.", + }, + "with an expired token response": { + errCode: "expired_token", + expectedErr: "Your request has expired. Please try logging in again.", + }, + "with an error response": { + errCode: "error_code", + expectedErr: "We cannot complete your request.", + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + input, _ := json.Marshal(map[string]string{ + "deviceCode": "test-device-code", + }) + output, _ := json.Marshal(map[string]string{ + "code": tt.errCode, + "message": "error message", + }) + responseErr := errors.NewError(string(output)) + mockClient := mockClient{} + mockClient.On( + "MakeRequest", + "POST", + "http://test.com/internal/device-authorization/token", + input, + ).Return([]byte(""), responseErr) + + _, err := login.FetchToken( + &mockClient, + "test-device-code", + "http://test.com", + 1*time.Microsecond, + 2, + ) + + assert.EqualError(t, err, tt.expectedErr) }) - responseErr := errors.NewError(string(output)) - mockClient := mockClient{} - mockClient.On( - "MakeRequest", - "POST", - "http://test.com/internal/device-authorization/token", - input, - ).Return([]byte(""), responseErr) - - _, err := login.FetchToken( - &mockClient, - "test-device-code", - "http://test.com", - 1*time.Microsecond, - 2, - ) - - assert.EqualError(t, err, "We cannot complete your request.") - }) + } } From 74f8757b26f2bd7dc359ed945b33505167fda718 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 24 Jun 2024 09:41:55 -0700 Subject: [PATCH 5/8] Add comments --- internal/login/login.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/login/login.go b/internal/login/login.go index 8756d966..773da96d 100644 --- a/internal/login/login.go +++ b/internal/login/login.go @@ -76,6 +76,8 @@ func (c Client) MakeRequest( return body, nil } +// FetchDeviceAuthorization makes a request to create a device authorization that will later be +// used to set a local access token if the user grants access. func FetchDeviceAuthorization( client UnauthenticatedClient, clientID string, @@ -105,6 +107,9 @@ func FetchDeviceAuthorization( return deviceAuthorization, nil } +// FetchToken attempts to get an access token. It will continue to try while the user logs in to +// verify their request. If the user denies the request or does nothing long enough for this call +// to time out, we do not return an access token. func FetchToken( client UnauthenticatedClient, deviceCode string, From 5a77d41d7f0ddb5d9f5a3ad7daf507bd46d67d4a Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 24 Jun 2024 09:44:04 -0700 Subject: [PATCH 6/8] Update comment --- internal/login/login.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/login/login.go b/internal/login/login.go index 773da96d..f67ca702 100644 --- a/internal/login/login.go +++ b/internal/login/login.go @@ -14,7 +14,7 @@ import ( const ( ClientID = "e6506150369268abae3ed46152687201" - MaxFetchTokenAttempts = 120 // 2 minutes + MaxFetchTokenAttempts = 120 // two minutes assuming interval is one second TokenInterval = 1 * time.Second ) From 19715a8ba836772dc378f44ebb81c7982f3b195a Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 24 Jun 2024 09:48:29 -0700 Subject: [PATCH 7/8] Update token attempts --- internal/login/login.go | 2 +- internal/login/login_test.go | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/login/login.go b/internal/login/login.go index f67ca702..87884000 100644 --- a/internal/login/login.go +++ b/internal/login/login.go @@ -119,7 +119,7 @@ func FetchToken( ) (DeviceAuthorizationToken, error) { var attempts int for { - if attempts >= maxAttempts { + if attempts > maxAttempts { return DeviceAuthorizationToken{}, errors.NewError("The request timed-out after too many attempts.") } deviceAuthorizationToken, err := fetchToken( diff --git a/internal/login/login_test.go b/internal/login/login_test.go index 403ee373..fbeeff39 100644 --- a/internal/login/login_test.go +++ b/internal/login/login_test.go @@ -65,6 +65,8 @@ func TestFetchDeviceAuthorization(t *testing.T) { func TestFetchToken(t *testing.T) { t.Run("with a token response", func(t *testing.T) { + minimalDuration := 1 * time.Microsecond + minimalAttempts := 1 input, _ := json.Marshal(map[string]string{ "deviceCode": "test-device-code", }) @@ -83,8 +85,8 @@ func TestFetchToken(t *testing.T) { &mockClient, "test-device-code", "http://test.com", - 1*time.Microsecond, - 2, + minimalDuration, + minimalAttempts, ) require.NoError(t, err) @@ -116,6 +118,8 @@ func TestFetchToken_WithError(t *testing.T) { } for name, tt := range tests { t.Run(name, func(t *testing.T) { + minimalDuration := 1 * time.Microsecond + minimalAttempts := 1 input, _ := json.Marshal(map[string]string{ "deviceCode": "test-device-code", }) @@ -136,8 +140,8 @@ func TestFetchToken_WithError(t *testing.T) { &mockClient, "test-device-code", "http://test.com", - 1*time.Microsecond, - 2, + minimalDuration, + minimalAttempts, ) assert.EqualError(t, err, tt.expectedErr) From 900ddbfaaf1fa3d0262bf2b14b5188f6baa3c695 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 24 Jun 2024 11:19:00 -0700 Subject: [PATCH 8/8] Update error messages --- internal/login/login.go | 4 ++-- internal/login/login_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/login/login.go b/internal/login/login.go index 87884000..8f145272 100644 --- a/internal/login/login.go +++ b/internal/login/login.go @@ -120,7 +120,7 @@ func FetchToken( var attempts int for { if attempts > maxAttempts { - return DeviceAuthorizationToken{}, errors.NewError("The request timed-out after too many attempts.") + return DeviceAuthorizationToken{}, errors.NewError("The request timed out after too many attempts.") } deviceAuthorizationToken, err := fetchToken( client, @@ -143,7 +143,7 @@ func FetchToken( case "authorization_pending": attempts += 1 case "access_denied": - return DeviceAuthorizationToken{}, errors.NewError("Your request has been denied. Please try logging in again.") + return DeviceAuthorizationToken{}, errors.NewError("Your request has been denied.") case "expired_token": return DeviceAuthorizationToken{}, errors.NewError("Your request has expired. Please try logging in again.") default: diff --git a/internal/login/login_test.go b/internal/login/login_test.go index fbeeff39..0a94be4f 100644 --- a/internal/login/login_test.go +++ b/internal/login/login_test.go @@ -101,11 +101,11 @@ func TestFetchToken_WithError(t *testing.T) { }{ "with an authorization pending response": { errCode: "authorization_pending", - expectedErr: "The request timed-out after too many attempts.", + expectedErr: "The request timed out after too many attempts.", }, "with an access denied response": { errCode: "access_denied", - expectedErr: "Your request has been denied. Please try logging in again.", + expectedErr: "Your request has been denied.", }, "with an expired token response": { errCode: "expired_token",