From 69fa65ccf1ad0940687b19ed255a6c2b00abd2f1 Mon Sep 17 00:00:00 2001 From: Sosokker Date: Wed, 12 Mar 2025 14:57:45 +0700 Subject: [PATCH] feat: add validation of input data in auth --- backend/internal/api/auth.go | 66 ++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/backend/internal/api/auth.go b/backend/internal/api/auth.go index b202f83..bb80493 100644 --- a/backend/internal/api/auth.go +++ b/backend/internal/api/auth.go @@ -4,17 +4,18 @@ import ( "context" "errors" "net/http" + "regexp" "github.com/danielgtaylor/huma/v2" "github.com/forfarm/backend/internal/domain" "github.com/forfarm/backend/internal/utilities" "github.com/go-chi/chi/v5" + validation "github.com/go-ozzo/ozzo-validation/v4" "golang.org/x/crypto/bcrypt" ) func (a *api) registerAuthRoutes(_ chi.Router, api huma.API) { tags := []string{"auth"} - prefix := "/auth" huma.Register(api, huma.Operation{ @@ -34,34 +35,61 @@ func (a *api) registerAuthRoutes(_ chi.Router, api huma.API) { type LoginInput struct { Body struct { - Email string `json:"email" example:" Email address of the user"` - Password string `json:"password" example:" Password of the user"` + Email string `json:"email" example:"Email address of the user"` + Password string `json:"password" example:"Password of the user"` } } type LoginOutput struct { Body struct { - Token string `json:"token" example:" JWT token for the user"` + Token string `json:"token" example:"JWT token for the user"` } } type RegisterInput struct { Body struct { - Email string `json:"email" example:" Email address of the user"` - Password string `json:"password" example:" Password of the user"` + Email string `json:"email" example:"Email address of the user"` + Password string `json:"password" example:"Password of the user"` } } type RegisterOutput struct { Body struct { - Token string `json:"token" example:" JWT token for the user"` + Token string `json:"token" example:"JWT token for the user"` } } +func validateEmail(email string) error { + return validation.Validate(email, + validation.Required.Error("email is required"), + validation.Match(regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`)).Error("invalid email format"), + ) +} + +func validatePassword(password string) error { + return validation.Validate(password, + validation.Required.Error("password is required"), + validation.Length(8, 0).Error("password must be at least 8 characters long"), + validation.Match(regexp.MustCompile(`[A-Z]`)).Error("password must contain at least one uppercase letter"), + validation.Match(regexp.MustCompile(`[a-z]`)).Error("password must contain at least one lowercase letter"), + validation.Match(regexp.MustCompile(`[0-9]`)).Error("password must contain at least one numeral"), + validation.Match(regexp.MustCompile(`[\W_]`)).Error("password must contain at least one special character"), + ) +} + func (a *api) registerHandler(ctx context.Context, input *RegisterInput) (*RegisterOutput, error) { resp := &RegisterOutput{} - // TODO: Validate input data + if input == nil { + return nil, errors.New("invalid input") + } + + if err := validateEmail(input.Body.Email); err != nil { + return nil, err + } + if err := validatePassword(input.Body.Password); err != nil { + return nil, err + } _, err := a.userRepo.GetByEmail(ctx, input.Body.Email) if err == domain.ErrNotFound { @@ -78,7 +106,12 @@ func (a *api) registerHandler(ctx context.Context, input *RegisterInput) (*Regis return nil, err } - token, err := utilities.CreateJwtToken(input.Body.Email) + user, err := a.userRepo.GetByEmail(ctx, input.Body.Email) + if err != nil { + return nil, err + } + + token, err := utilities.CreateJwtToken(user.UUID) if err != nil { return nil, err } @@ -93,14 +126,25 @@ func (a *api) registerHandler(ctx context.Context, input *RegisterInput) (*Regis func (a *api) loginHandler(ctx context.Context, input *LoginInput) (*LoginOutput, error) { resp := &LoginOutput{} - // TODO: Validate input data + if input == nil { + return nil, errors.New("invalid input") + } + if input.Body.Email == "" { + return nil, errors.New("email field is required") + } + if input.Body.Password == "" { + return nil, errors.New("password field is required") + } + + if err := validateEmail(input.Body.Email); err != nil { + return nil, err + } user, err := a.userRepo.GetByEmail(ctx, input.Body.Email) if err != nil { return nil, err } - // verify password hash if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(input.Body.Password)); err != nil { return nil, err }