diff --git a/app/package-lock.json b/app/package-lock.json index 62ac19d..fec1e8d 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@authorizerdev/authorizer-react": "^1.1.18", + "@authorizerdev/authorizer-react": "^1.1.19", "@types/react": "^17.0.15", "@types/react-dom": "^17.0.9", "esbuild": "^0.12.17", @@ -41,9 +41,9 @@ } }, "node_modules/@authorizerdev/authorizer-react": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.18.tgz", - "integrity": "sha512-5SgFzG1VatmrMpl9XKwPcoVmCayA4Hn+sd2I9CwRlCWkdcna4pGJL8kYesuIGjGagS9394qp4ICRLRZ35wXj8A==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.19.tgz", + "integrity": "sha512-hbId4mtzeWke1uUFAZrPwT45UmxgTp0QHAAsQvl/I0+mgoCJlJdAnUBCiJD6d5lVHJk41nx/ePYG4rw2Aj6HTw==", "dependencies": { "@authorizerdev/authorizer-js": "^1.2.18", "validator": "^13.11.0" diff --git a/app/package.json b/app/package.json index 530b65c..95fb694 100644 --- a/app/package.json +++ b/app/package.json @@ -12,7 +12,7 @@ "author": "Lakhan Samani", "license": "ISC", "dependencies": { - "@authorizerdev/authorizer-react": "^1.1.18", + "@authorizerdev/authorizer-react": "^1.1.19", "@types/react": "^17.0.15", "@types/react-dom": "^17.0.9", "esbuild": "^0.12.17", diff --git a/app/yarn.lock b/app/yarn.lock index d9fa871..fcb882e 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -9,10 +9,10 @@ dependencies: cross-fetch "^3.1.5" -"@authorizerdev/authorizer-react@^1.1.18": - version "1.1.18" - resolved "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.18.tgz" - integrity sha512-5SgFzG1VatmrMpl9XKwPcoVmCayA4Hn+sd2I9CwRlCWkdcna4pGJL8kYesuIGjGagS9394qp4ICRLRZ35wXj8A== +"@authorizerdev/authorizer-react@^1.1.19": + version "1.1.19" + resolved "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.19.tgz" + integrity sha512-hbId4mtzeWke1uUFAZrPwT45UmxgTp0QHAAsQvl/I0+mgoCJlJdAnUBCiJD6d5lVHJk41nx/ePYG4rw2Aj6HTw== dependencies: "@authorizerdev/authorizer-js" "^1.2.18" validator "^13.11.0" diff --git a/dashboard/src/components/EnvComponents/Features.tsx b/dashboard/src/components/EnvComponents/Features.tsx index 77553bf..4c72693 100644 --- a/dashboard/src/components/EnvComponents/Features.tsx +++ b/dashboard/src/components/EnvComponents/Features.tsx @@ -108,11 +108,10 @@ const Features = ({ variables, setVariables }: any) => { /> - {/** TODO enable after final release */} - {/* {!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && ( + {!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && ( - TOTP: + Time Based OTP (TOTP): Note: to enable totp mfa @@ -125,7 +124,7 @@ const Features = ({ variables, setVariables }: any) => { /> - )} */} + )} {!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && ( diff --git a/server/handlers/verify_email.go b/server/handlers/verify_email.go index 47f61d3..34f9d9f 100644 --- a/server/handlers/verify_email.go +++ b/server/handlers/verify_email.go @@ -74,7 +74,13 @@ func VerifyEmailHandler() gin.HandlerFunc { now := time.Now().Unix() user.EmailVerifiedAt = &now isSignUp = true - db.Provider.UpdateUser(c, user) + user, err = db.Provider.UpdateUser(c, user) + if err != nil { + log.Debug("Error updating user: ", err) + errorRes["error"] = err.Error() + utils.HandleRedirectORJsonResponse(c, http.StatusBadRequest, errorRes, generateRedirectURL(redirectURL, errorRes)) + return + } } // delete from verification table db.Provider.DeleteVerificationRequest(c, verificationRequest) diff --git a/server/resolvers/login.go b/server/resolvers/login.go index b940370..74b669f 100644 --- a/server/resolvers/login.go +++ b/server/resolvers/login.go @@ -77,7 +77,7 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes } if err != nil { log.Debug("Failed to get user: ", err) - return res, fmt.Errorf(`bad user credentials`) + return res, fmt.Errorf(`user not found`) } if user.RevokedTimestamp != nil { log.Debug("User access is revoked") diff --git a/server/resolvers/signup.go b/server/resolvers/signup.go index 8eb3368..f947f2c 100644 --- a/server/resolvers/signup.go +++ b/server/resolvers/signup.go @@ -73,7 +73,7 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR } isEmailSignup := email != "" isMobileSignup := phoneNumber != "" - if isBasicAuthDisabled { + if isBasicAuthDisabled && isEmailSignup { log.Debug("Basic authentication is disabled") return res, fmt.Errorf(`basic authentication is disabled for this instance`) } @@ -222,12 +222,12 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR log.Debug("Error getting email verification disabled: ", err) isEmailVerificationDisabled = true } - if isEmailVerificationDisabled { + if isEmailVerificationDisabled && isEmailSignup { now := time.Now().Unix() user.EmailVerifiedAt = &now } disablePhoneVerification, _ := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisablePhoneVerification) - if disablePhoneVerification { + if disablePhoneVerification && isMobileSignup { now := time.Now().Unix() user.PhoneNumberVerifiedAt = &now } diff --git a/server/resolvers/verify_otp.go b/server/resolvers/verify_otp.go index e056dee..16a10c7 100644 --- a/server/resolvers/verify_otp.go +++ b/server/resolvers/verify_otp.go @@ -36,24 +36,29 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod return res, fmt.Errorf(`invalid session: %s`, err.Error()) } - if refs.StringValue(params.Email) == "" && refs.StringValue(params.PhoneNumber) == "" { + email := strings.TrimSpace(refs.StringValue(params.Email)) + phoneNumber := strings.TrimSpace(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`) - } - currentField := models.FieldNameEmail - if refs.StringValue(params.Email) == "" { - currentField = models.FieldNamePhoneNumber + return res, fmt.Errorf(`email or phone number is required`) } + isEmailVerification := email != "" + isMobileVerification := phoneNumber != "" // Get user by email or phone number var user *models.User - if currentField == models.FieldNameEmail { + if isEmailVerification { user, err = db.Provider.GetUserByEmail(ctx, refs.StringValue(params.Email)) + if err != nil { + log.Debug("Failed to get user by email: ", err) + } } else { user, err = db.Provider.GetUserByPhoneNumber(ctx, refs.StringValue(params.PhoneNumber)) + if err != nil { + log.Debug("Failed to get user by phone number: ", err) + } } if user == nil || err != nil { - log.Debug("Failed to get user by email or phone number: ", err) - return res, err + return res, fmt.Errorf(`user not found`) } // Verify OTP based on TOPT or OTP if refs.BoolValue(params.IsTotp) { @@ -78,14 +83,19 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod } } else { var otp *models.OTP - if currentField == models.FieldNameEmail { + if isEmailVerification { otp, err = db.Provider.GetOTPByEmail(ctx, refs.StringValue(params.Email)) + if err != nil { + log.Debug(`Failed to get otp request for email: `, err.Error()) + } } else { otp, err = db.Provider.GetOTPByPhoneNumber(ctx, refs.StringValue(params.PhoneNumber)) + if err != nil { + log.Debug(`Failed to get otp request for phone number: `, err.Error()) + } } if otp == nil && err != nil { - log.Debugf("Failed to get otp request for %s: %s", currentField, err.Error()) - return res, fmt.Errorf(`invalid %s: %s`, currentField, err.Error()) + return res, fmt.Errorf(`OTP not found`) } if params.Otp != otp.Otp { log.Debug("Failed to verify otp request: Incorrect value") @@ -104,10 +114,26 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod return res, fmt.Errorf(`invalid session: %s`, err.Error()) } - isSignUp := user.EmailVerifiedAt == nil && user.PhoneNumberVerifiedAt == nil - // TODO - Add Login method in DB when we introduce OTP for social media login + isSignUp := false + if user.EmailVerifiedAt == nil && isEmailVerification { + isSignUp = true + now := time.Now().Unix() + user.EmailVerifiedAt = &now + } + if user.PhoneNumberVerifiedAt == nil && isMobileVerification { + isSignUp = true + now := time.Now().Unix() + user.PhoneNumberVerifiedAt = &now + } + if isSignUp { + user, err = db.Provider.UpdateUser(ctx, user) + if err != nil { + log.Debug("Failed to update user: ", err) + return res, err + } + } loginMethod := constants.AuthRecipeMethodBasicAuth - if currentField == models.FieldNamePhoneNumber { + if isMobileVerification { loginMethod = constants.AuthRecipeMethodMobileOTP } roles := strings.Split(user.Roles, ",") diff --git a/server/test/mobile_signup_test.go b/server/test/mobile_signup_test.go index 949895b..7bfba1c 100644 --- a/server/test/mobile_signup_test.go +++ b/server/test/mobile_signup_test.go @@ -98,12 +98,17 @@ func mobileSingupTest(t *testing.T, s TestSetup) { }) assert.Nil(t, err) assert.NotEmpty(t, otpRes.Message) + // Check if phone number is verified + user, err = db.Provider.GetUserByPhoneNumber(ctx, phoneNumber) + assert.NoError(t, err) + assert.NotNil(t, user) + assert.NotNil(t, user.PhoneNumberVerifiedAt) res, err = resolvers.SignupResolver(ctx, model.SignUpInput{ PhoneNumber: refs.NewStringRef(phoneNumber), Password: s.TestInfo.Password, ConfirmPassword: s.TestInfo.Password, }) - assert.Error(t, err) + assert.Error(t, err, "should throw duplicate error") assert.Nil(t, res) cleanData("1234567890@authorizer.dev") }) diff --git a/server/test/verify_email_test.go b/server/test/verify_email_test.go index 491f349..19416c1 100644 --- a/server/test/verify_email_test.go +++ b/server/test/verify_email_test.go @@ -35,7 +35,11 @@ func verifyEmailTest(t *testing.T, s TestSetup) { }) assert.Nil(t, err) assert.NotEqual(t, verifyRes.AccessToken, "", "access token should not be empty") - + // Check if phone number is verified + user1, err := db.Provider.GetUserByEmail(ctx, email) + assert.NoError(t, err) + assert.NotNil(t, user1) + assert.NotNil(t, user1.EmailVerifiedAt) cleanData(email) }) }