From 93f585a33ead053c0adc1b2f6a61ec14f53ed596 Mon Sep 17 00:00:00 2001 From: Abhishek Chatterjee Date: Sat, 29 Aug 2026 20:23:44 +0530 Subject: [PATCH] feat(auth): #67: loosen username validation to allow letters, digits, underscores, and hyphens --- frontend/src/lib/validations.ts | 7 +++++-- internal/features/auth/dto.go | 6 +++--- internal/features/auth/service.go | 14 ++++++++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/frontend/src/lib/validations.ts b/frontend/src/lib/validations.ts index 936e271..0da2b5b 100644 --- a/frontend/src/lib/validations.ts +++ b/frontend/src/lib/validations.ts @@ -1,13 +1,16 @@ import { z } from 'zod'; -// Username validation: lowercase letters only, 3-50 characters +// Username validation: letters, numbers, underscores, hyphens; 3-50 characters const usernameSchema = z .string({ message: 'Username must be a string', }) .min(3, 'Username must be at least 3 characters') .max(50, 'Username must be at most 50 characters') - .regex(/^[a-z]+$/, 'Username must contain only lowercase letters'); + .regex( + /^[a-zA-Z0-9_-]+$/, + 'Username can only contain letters, numbers, underscores, and hyphens' + ); // Password validation: min 8 chars, must contain uppercase, lowercase, number, and symbol const passwordSchema = z diff --git a/internal/features/auth/dto.go b/internal/features/auth/dto.go index a2bf33a..53a6bc8 100644 --- a/internal/features/auth/dto.go +++ b/internal/features/auth/dto.go @@ -8,14 +8,14 @@ import ( // tags are enforced by go-playground/validator in Service.Register; the DB // config is validated separately (type-specific fields). type RegisterInput struct { - Username string `validate:"required,min=3,max=50,lowercase,alpha"` + Username string `validate:"required,min=3,max=50,username_format"` Password string `validate:"required,min=8,password_strength"` DBConfig dbclient.Config `json:"DBConfig"` } // LoginInput is the payload expected when signing in an existing account. type LoginInput struct { - Username string `validate:"required,lowercase,alpha"` + Username string `validate:"required,username_format"` Password string `validate:"required"` } @@ -23,7 +23,7 @@ type LoginInput struct { // password. The user must prove ownership by supplying the recovery key that // was shown at registration time. type ResetPasswordInput struct { - Username string `validate:"required,lowercase,alpha"` + Username string `validate:"required,username_format"` NewPassword string `validate:"required,min=8,password_strength"` RecoveryKey string `validate:"required"` } diff --git a/internal/features/auth/service.go b/internal/features/auth/service.go index 1d82170..ad01ac0 100644 --- a/internal/features/auth/service.go +++ b/internal/features/auth/service.go @@ -71,6 +71,15 @@ func (s *Service) Startup(ctx context.Context) { s.ctx = ctx } +var usernameRegex = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`) + +// validateUsernameFormat enforces that a username contains only letters, +// digits, underscores, and hyphens. It is registered as the "username_format" +// validator rule. +func validateUsernameFormat(fl validator.FieldLevel) bool { + return usernameRegex.MatchString(fl.Field().String()) +} + // validatePasswordStrength enforces that a password contains at least one // uppercase letter, one lowercase letter, one digit and one symbol. It is // registered as the "password_strength" validator rule. @@ -91,12 +100,13 @@ func validatePasswordStrength(fl validator.FieldLevel) bool { // NewService wires a shared connection holder, the database-credentials // keyring repository, the master-key keyring repository and a validator with -// the custom password strength rule into a ready-to-use auth Service. +// the custom validation rules into a ready-to-use auth Service. func NewService(conn *dbclient.Connection) *Service { validate := validator.New() - // Register custom password strength validator + // Register custom validators _ = validate.RegisterValidation("password_strength", validatePasswordStrength) + _ = validate.RegisterValidation("username_format", validateUsernameFormat) return &Service{ conn: conn,