diff --git a/internal/api/mfa.go b/internal/api/mfa.go index d6158e964..ec49b3981 100644 --- a/internal/api/mfa.go +++ b/internal/api/mfa.go @@ -572,11 +572,25 @@ func (a *API) verifyPhoneFactor(w http.ResponseWriter, r *http.Request, params * } return unprocessableEntityError(ErrorCodeMFAChallengeExpired, "MFA challenge %v has expired, verify against another challenge or create a new challenge.", challenge.ID) } - otpCode, shouldReEncrypt, err := challenge.GetOtpCode(config.Security.DBEncryption.DecryptionKeys, config.Security.DBEncryption.Encrypt, config.Security.DBEncryption.EncryptionKeyID) - if err != nil { - return internalServerError("Database error verifying MFA TOTP secret").WithInternalError(err) + var valid bool + var otpCode string + var shouldReEncrypt bool + if config.Sms.IsTwilioVerifyProvider() { + smsProvider, err := sms_provider.GetSmsProvider(*config) + if err != nil { + return internalServerError("Failed to get SMS provider").WithInternalError(err) + } + if err := smsProvider.VerifyOTP(factor.Phone.String(), params.Code); err != nil { + return forbiddenError(ErrorCodeOTPExpired, "Token has expired or is invalid").WithInternalError(err) + } + valid = true + } else { + otpCode, shouldReEncrypt, err = challenge.GetOtpCode(config.Security.DBEncryption.DecryptionKeys, config.Security.DBEncryption.Encrypt, config.Security.DBEncryption.EncryptionKeyID) + if err != nil { + return internalServerError("Database error verifying MFA TOTP secret").WithInternalError(err) + } + valid = subtle.ConstantTimeCompare([]byte(otpCode), []byte(params.Code)) == 1 } - valid := subtle.ConstantTimeCompare([]byte(otpCode), []byte(params.Code)) == 1 if config.Hook.MFAVerificationAttempt.Enabled { input := hooks.MFAVerificationAttemptInput{ UserID: user.ID, diff --git a/internal/api/phone_test.go b/internal/api/phone_test.go index 468c39955..adc50f1a9 100644 --- a/internal/api/phone_test.go +++ b/internal/api/phone_test.go @@ -35,6 +35,9 @@ func (t *TestSmsProvider) SendMessage(phone, message, channel, otp string) (stri t.SentMessages += 1 return "", nil } +func (t *TestSmsProvider) VerifyOTP(phone, otp string) error { + return nil +} func TestPhone(t *testing.T) { api, config, err := setupAPIForTest() diff --git a/internal/api/sms_provider/messagebird.go b/internal/api/sms_provider/messagebird.go index 61796a1f6..05f793903 100644 --- a/internal/api/sms_provider/messagebird.go +++ b/internal/api/sms_provider/messagebird.go @@ -109,3 +109,7 @@ func (t *MessagebirdProvider) SendSms(phone string, message string) (string, err return resp.ID, nil } + +func (t *MessagebirdProvider) VerifyOTP(phone, code string) error { + return fmt.Errorf("VerifyOTP is not supported for Messagebird") +} diff --git a/internal/api/sms_provider/sms_provider.go b/internal/api/sms_provider/sms_provider.go index 1643991f5..103db4f95 100644 --- a/internal/api/sms_provider/sms_provider.go +++ b/internal/api/sms_provider/sms_provider.go @@ -30,6 +30,7 @@ func init() { type SmsProvider interface { SendMessage(phone, message, channel, otp string) (string, error) + VerifyOTP(phone, token string) error } func GetSmsProvider(config conf.GlobalConfiguration) (SmsProvider, error) { diff --git a/internal/api/sms_provider/textlocal.go b/internal/api/sms_provider/textlocal.go index 56177f0fa..ef07a6f39 100644 --- a/internal/api/sms_provider/textlocal.go +++ b/internal/api/sms_provider/textlocal.go @@ -102,3 +102,6 @@ func (t *TextlocalProvider) SendSms(phone string, message string) (string, error return messageID, nil } +func (t *TextlocalProvider) VerifyOTP(phone, code string) error { + return fmt.Errorf("VerifyOTP is not supported for Textlocal") +} diff --git a/internal/api/sms_provider/twilio.go b/internal/api/sms_provider/twilio.go index 3705635d8..3536c2f17 100644 --- a/internal/api/sms_provider/twilio.go +++ b/internal/api/sms_provider/twilio.go @@ -136,3 +136,6 @@ func (t *TwilioProvider) SendSms(phone, message, channel, otp string) (string, e return resp.MessageSID, nil } +func (t *TwilioProvider) VerifyOTP(phone, code string) error { + return fmt.Errorf("VerifyOTP is not supported for Twilio") +} diff --git a/internal/api/sms_provider/vonage.go b/internal/api/sms_provider/vonage.go index 69ef3e0a6..4b9fd5b74 100644 --- a/internal/api/sms_provider/vonage.go +++ b/internal/api/sms_provider/vonage.go @@ -99,3 +99,7 @@ func (t *VonageProvider) SendSms(phone string, message string) (string, error) { return resp.Messages[0].MessageID, nil } + +func (t *VonageProvider) VerifyOTP(phone, code string) error { + return fmt.Errorf("VerifyOTP is not supported for Vonage") +}