feat: add testing & ui for forgot password with mobile

This commit is contained in:
Lakhan Samani
2023-12-22 01:26:14 +05:30
parent c9c2789097
commit 02f7a62918
15 changed files with 469 additions and 120 deletions

View File

@@ -6,9 +6,11 @@ import (
"strings"
"time"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/db/models"
mailService "github.com/authorizerdev/authorizer/server/email"
@@ -16,18 +18,17 @@ import (
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/parsers"
"github.com/authorizerdev/authorizer/server/refs"
"github.com/authorizerdev/authorizer/server/smsproviders"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils"
)
// ForgotPasswordResolver is a resolver for forgot password mutation
func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) {
var res *model.Response
func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInput) (*model.ForgotPasswordResponse, error) {
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return res, err
return nil, err
}
isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication)
@@ -37,7 +38,7 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
}
isEmailVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification)
if err != nil {
log.Debug("Error getting basic auth disabled: ", err)
log.Debug("Error getting email verification disabled: ", err)
isEmailVerificationDisabled = true
}
@@ -48,7 +49,7 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
}
isMobileVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisablePhoneVerification)
if err != nil {
log.Debug("Error getting mobile basic auth disabled: ", err)
log.Debug("Error getting mobile verification disabled: ", err)
isMobileVerificationDisabled = true
}
@@ -56,7 +57,7 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
phoneNumber := refs.StringValue(params.PhoneNumber)
if email == "" && phoneNumber == "" {
log.Debug("Email or phone number is required")
return res, fmt.Errorf(`email or phone number is required`)
return nil, fmt.Errorf(`email or phone number is required`)
}
log := log.WithFields(log.Fields{
"email": refs.StringValue(params.Email),
@@ -66,11 +67,11 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
isMobileLogin := phoneNumber != ""
if isBasicAuthDisabled && isEmailLogin && !isEmailVerificationDisabled {
log.Debug("Basic authentication is disabled.")
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
return nil, fmt.Errorf(`basic authentication is disabled for this instance`)
}
if isMobileBasicAuthDisabled && isMobileLogin && !isMobileVerificationDisabled {
log.Debug("Mobile basic authentication is disabled.")
return res, fmt.Errorf(`mobile basic authentication is disabled for this instance`)
return nil, fmt.Errorf(`mobile basic authentication is disabled for this instance`)
}
var user *models.User
if isEmailLogin {
@@ -80,17 +81,17 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
}
if err != nil {
log.Debug("Failed to get user: ", err)
return res, fmt.Errorf(`bad user credentials`)
return nil, fmt.Errorf(`bad user credentials`)
}
hostname := parsers.GetHost(gc)
_, nonceHash, err := utils.GenerateNonce()
if err != nil {
log.Debug("Failed to generate nonce: ", err)
return res, err
return nil, err
}
if user.RevokedTimestamp != nil {
log.Debug("User access is revoked")
return res, fmt.Errorf(`user access has been revoked`)
return nil, fmt.Errorf(`user access has been revoked`)
}
if isEmailLogin {
redirectURI := ""
@@ -108,7 +109,7 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
verificationToken, err := token.CreateVerificationToken(email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURI)
if err != nil {
log.Debug("Failed to create verification token", err)
return res, err
return nil, err
}
_, err = db.Provider.AddVerificationRequest(ctx, &models.VerificationRequest{
Token: verificationToken,
@@ -120,7 +121,7 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
})
if err != nil {
log.Debug("Failed to add verification request", err)
return res, err
return nil, err
}
// execute it as go routine so that we can reduce the api latency
go mailService.SendEmail([]string{email}, constants.VerificationTypeForgotPassword, map[string]interface{}{
@@ -128,13 +129,42 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
"organization": utils.GetOrganization(),
"verification_url": utils.GetForgotPasswordURL(verificationToken, redirectURI),
})
return &model.ForgotPasswordResponse{
Message: `Please check your inbox! We have sent a password reset link.`,
}, nil
}
if isMobileLogin {
// TODO: send sms
expiresAt := time.Now().Add(1 * time.Minute).Unix()
otp := utils.GenerateOTP()
otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{
Email: refs.StringValue(user.Email),
PhoneNumber: refs.StringValue(user.PhoneNumber),
Otp: otp,
ExpiresAt: expiresAt,
})
if err != nil {
log.Debug("Failed to add otp: ", err)
return nil, err
}
mfaSession := uuid.NewString()
err = memorystore.Provider.SetMfaSession(user.ID, mfaSession, expiresAt)
if err != nil {
log.Debug("Failed to add mfasession: ", err)
return nil, err
}
cookie.SetMfaSession(gc, mfaSession)
smsBody := strings.Builder{}
smsBody.WriteString("Your verification code is: ")
smsBody.WriteString(otpData.Otp)
if err := smsproviders.SendSMS(phoneNumber, smsBody.String()); err != nil {
log.Debug("Failed to send sms: ", err)
// continue
}
return &model.ForgotPasswordResponse{
Message: "Please enter the OTP sent to your phone number and change your password.",
ShouldShowMobileOtpScreen: refs.NewBoolRef(true),
}, nil
}
res = &model.Response{
Message: `Please check your inbox! We have sent a password reset link.`,
}
return res, nil
return nil, fmt.Errorf(`email or phone number verification needs to be enabled`)
}