add forget password

This commit is contained in:
aditya.siregar
2024-07-23 01:36:25 +07:00
parent 7ea809cc09
commit 5a0dec6128
22 changed files with 907 additions and 76 deletions
+74 -1
View File
@@ -1,6 +1,7 @@
package auth
import (
"fmt"
"furtuna-be/internal/constants/role"
"net/http"
@@ -19,6 +20,8 @@ type AuthHandler struct {
func (a *AuthHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
authRoute := group.Group("/auth")
authRoute.POST("/login", a.AuthLogin)
authRoute.POST("/forgot-password", a.ForgotPassword)
authRoute.POST("/reset-password", jwt, a.ResetPassword)
}
func NewAuthHandler(service services.Auth) *AuthHandler {
@@ -77,7 +80,8 @@ func (h *AuthHandler) AuthLogin(c *gin.Context) {
ID: int64(authUser.RoleID),
Role: authUser.RoleName,
},
Site: site,
Site: site,
ResetPassword: authUser.ResetPassword,
}
c.JSON(http.StatusOK, response.BaseResponse{
@@ -87,3 +91,72 @@ func (h *AuthHandler) AuthLogin(c *gin.Context) {
Data: resp,
})
}
// ForgotPassword handles the request for password reset.
// @Summary Request password reset
// @Description Sends a password reset link to the user's email.
// @Accept json
// @Produce json
// @Param bodyParam body auth2.ForgotPasswordRequest true "User email"
// @Success 200 {object} response.BaseResponse "Password reset link sent"
// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request"
// @Router /api/v1/auth/forgot-password [post]
// @Tags Auth Password API's
func (h *AuthHandler) ForgotPassword(c *gin.Context) {
var bodyParam auth2.ResetPasswordRequest
if err := c.ShouldBindJSON(&bodyParam); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
err := h.service.SendPasswordResetLink(c, bodyParam.Email)
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Message: "Password reset link sent",
})
}
// ResetPassword handles the password reset process.
// @Summary Reset user password
// @Description Resets the user's password using the provided token.
// @Accept json
// @Produce json
// @Param bodyParam body auth2.ResetPasswordRequest true "Reset password details"
// @Success 200 {object} response.BaseResponse "Password reset successful"
// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request"
// @Router /api/v1/auth/reset-password [post]
// @Tags Auth Password API's
func (h *AuthHandler) ResetPassword(c *gin.Context) {
ctx := auth2.GetMyContext(c)
var req auth2.ResetPasswordChangeRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
if err := req.Validate(); err != nil {
response.ErrorWrapper(c, errors.NewError(
errors.ErrorBadRequest.ErrorType(),
fmt.Sprintf("invalid request %v", err.Error())))
return
}
err := h.service.ResetPassword(ctx, req.OldPassword, req.NewPassword)
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Message: "Password reset successful",
})
}
+65 -2
View File
@@ -1,5 +1,11 @@
package request
import (
"errors"
"fmt"
"github.com/go-playground/validator/v10"
)
type LoginRequest struct {
Email string `json:"email"`
Password string `json:"password"`
@@ -10,6 +16,63 @@ type ResetPasswordRequest struct {
}
type ResetPasswordChangeRequest struct {
Token string `json:"token" validate:"required"`
Password string `json:"password" validate:"required"`
OldPassword string `json:"old_password" validate:"required"`
NewPassword string `json:"new_password" validate:"required,strongpwd"`
}
func (e *ResetPasswordChangeRequest) Validate() error {
validate := validator.New()
validate.RegisterValidation("strongpwd", validateStrongPassword)
if err := validate.Struct(e); err != nil {
// Handle the validation errors
for _, err := range err.(validator.ValidationErrors) {
switch err.Field() {
case "NewPassword":
return fmt.Errorf("%w", validatePasswordError(err.Tag()))
default:
return fmt.Errorf("validation failed: %w", err)
}
}
}
return nil
}
func validateStrongPassword(fl validator.FieldLevel) bool {
password := fl.Field().String()
var (
hasMinLen = len(password) >= 8
)
return hasMinLen
}
// Error messages for password validation
var (
ErrPasswordTooShort = errors.New("password must be at least 8 characters long")
ErrPasswordNoUpper = errors.New("password must contain at least one uppercase letter")
ErrPasswordNoLower = errors.New("password must contain at least one lowercase letter")
ErrPasswordNoNumber = errors.New("password must contain at least one digit")
ErrPasswordNoSpecial = errors.New("password must contain at least one special character (!@#$%^&*)")
ErrPasswordValidation = errors.New("password does not meet the strength requirements")
)
func validatePasswordError(tag string) error {
switch tag {
case "min":
return ErrPasswordTooShort
case "uppercase":
return ErrPasswordNoUpper
case "lowercase":
return ErrPasswordNoLower
case "number":
return ErrPasswordNoNumber
case "special":
return ErrPasswordNoSpecial
default:
return ErrPasswordValidation
}
}
+6 -5
View File
@@ -1,11 +1,12 @@
package response
type LoginResponse struct {
Token string `json:"token"`
Name string `json:"name"`
Role Role `json:"role"`
Partner *Partner `json:"partner"`
Site *SiteName `json:"site,omitempty"`
Token string `json:"token"`
Name string `json:"name"`
Role Role `json:"role"`
Partner *Partner `json:"partner"`
Site *SiteName `json:"site,omitempty"`
ResetPassword bool `json:"reset_password"`
}
type Role struct {