diff --git a/cmd/auth.go b/cmd/auth.go index 0a805d74..2c11d3dd 100644 --- a/cmd/auth.go +++ b/cmd/auth.go @@ -78,7 +78,7 @@ func loginCmd() *cobra.Command { } dependencies.Logger.Verbose(). - Str("password", password). + Bool("passwordProvided", password != ""). Str("email", email). Str("authCode", util.IfEmpty(authCode, "")). Msg("logging in") diff --git a/pkg/appstore/appstore_login.go b/pkg/appstore/appstore_login.go index 419fdeaa..9cdbe887 100644 --- a/pkg/appstore/appstore_login.go +++ b/pkg/appstore/appstore_login.go @@ -150,8 +150,14 @@ func (t *appstore) parseLoginResponse(res *http.Result[loginResult], attempt int } else { err = NewErrorWithMetadata(errors.New("something went wrong"), res) } - } else if res.StatusCode != gohttp.StatusOK || res.Data.PasswordToken == "" || res.Data.DirectoryServicesID == "" { - err = NewErrorWithMetadata(errors.New("something went wrong"), res) + } else if res.StatusCode != gohttp.StatusOK { + err = NewErrorWithMetadata(fmt.Errorf("unexpected login status: %d", res.StatusCode), res) + } else if res.Data.PasswordToken == "" || res.Data.DirectoryServicesID == "" { + if authCode == "" { + err = ErrAuthCodeRequired + } else { + err = NewErrorWithMetadata(errors.New("login response did not include an App Store session token"), res) + } } return retry, redirect, err diff --git a/pkg/appstore/appstore_login_test.go b/pkg/appstore/appstore_login_test.go index 3521eeb1..89a038b1 100644 --- a/pkg/appstore/appstore_login_test.go +++ b/pkg/appstore/appstore_login_test.go @@ -124,6 +124,42 @@ var _ = Describe("AppStore (Login)", func() { }) }) + When("store API returns OK without a session token before 2FA", func() { + BeforeEach(func() { + mockClient.EXPECT(). + Send(gomock.Any()). + Return(http.Result[loginResult]{ + StatusCode: 200, + }, nil) + }) + + It("requests an auth code", func() { + _, err := as.Login(LoginInput{ + Password: testPassword, + }) + Expect(err).To(Equal(ErrAuthCodeRequired)) + }) + }) + + When("store API returns OK without a session token after 2FA", func() { + BeforeEach(func() { + mockClient.EXPECT(). + Send(gomock.Any()). + Return(http.Result[loginResult]{ + StatusCode: 200, + }, nil) + }) + + It("returns an actionable invalid session response error", func() { + _, err := as.Login(LoginInput{ + Password: testPassword, + AuthCode: "123456", + }) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("login response did not include an App Store session token")) + }) + }) + When("store API indicates account is disabled", func() { BeforeEach(func() { mockClient.EXPECT().