diff --git a/internal/api/verify.go b/internal/api/verify.go index ae0d05c42b..439c00c506 100644 --- a/internal/api/verify.go +++ b/internal/api/verify.go @@ -408,7 +408,13 @@ func (a *API) smsVerify(r *http.Request, conn *storage.Connection, user *models. switch params.Type { case smsVerification: - if terr := models.NewAuditLogEntry(config.AuditLog, r, tx, user, models.UserSignedUpAction, "", map[string]interface{}{ + // A phone that is already confirmed belongs to an existing user, + // so this verification is a sign-in, not a signup. + action := models.UserSignedUpAction + if user.IsPhoneConfirmed() { + action = models.LoginAction + } + if terr := models.NewAuditLogEntry(config.AuditLog, r, tx, user, action, "", map[string]interface{}{ "provider": PhoneProvider, }); terr != nil { return terr diff --git a/internal/api/verify_ott_parity_test.go b/internal/api/verify_ott_parity_test.go index c48b60bbc8..20f1656624 100644 --- a/internal/api/verify_ott_parity_test.go +++ b/internal/api/verify_ott_parity_test.go @@ -226,6 +226,14 @@ func (ts *VerifyTestSuite) TestVerifyOTPParityPhoneFlows() { Phone: parityPhone, } + phoneLoggedIn := otpParityOutcome{ + Status: http.StatusOK, + Action: string(models.LoginAction), + PhoneConfirmed: true, + Email: parityEmail, + Phone: parityPhone, + } + phoneChanged := otpParityOutcome{ Status: http.StatusOK, Action: string(models.UserModifiedAction), @@ -242,6 +250,16 @@ func (ts *VerifyTestSuite) TestVerifyOTPParityPhoneFlows() { requestBody: phoneOTPBody(smsVerification, parityPhone), expected: phoneSignedUp, }, + // An existing user signing in with an SMS code is a login. Only the + // verification that first confirms the phone is a signup. + "sms with a valid code signs a confirmed user in": { + seed: func(u *models.User) { + u.PhoneConfirmedAt = &now + ts.seedChallenge(u, models.ConfirmationToken, parityPhone, phoneHash, now, time.Hour) + }, + requestBody: phoneOTPBody(smsVerification, parityPhone), + expected: phoneLoggedIn, + }, "sms with an expired code is rejected": { seed: func(u *models.User) { ts.seedChallenge(u, models.ConfirmationToken, parityPhone, phoneHash, expired, -time.Hour)