diff --git a/modules/login.go b/modules/login.go index d58d5af..b9c4f62 100644 --- a/modules/login.go +++ b/modules/login.go @@ -8,8 +8,10 @@ import ( "evolve/util/auth" dbutil "evolve/util/db/user" "fmt" + "strings" "github.com/google/uuid" + "golang.org/x/crypto/bcrypt" ) type LoginReq struct { @@ -45,6 +47,12 @@ func (l *LoginReq) validate() error { return nil } +func isBcryptHash(s string) bool { + return strings.HasPrefix(s, "$2a$") || + strings.HasPrefix(s, "$2b$") || + strings.HasPrefix(s, "$2y$") +} + func (l *LoginReq) Login(ctx context.Context) (map[string]string, error) { logger := util.SharedLogger @@ -59,15 +67,39 @@ func (l *LoginReq) Login(ctx context.Context) (map[string]string, error) { } // id is UUID(16 bytes) Google's UUID. + // Query user and get password hash var id uuid.UUID var role string + var storedPasswordHash string + err = db.QueryRow(ctx, "SELECT id, role, password FROM users WHERE username = $1 OR email = $2", l.UserName, l.Email).Scan(&id, &role, &storedPasswordHash) - err = db.QueryRow(ctx, "SELECT id, role FROM users WHERE (username = $1 OR email = $2) AND password = $3", l.UserName, l.Email, l.Password).Scan(&id, &role) if err != nil { logger.Error(fmt.Sprintf("Login: failed to query user: %v", err), err) return nil, fmt.Errorf("invalid username/email or password") } + if isBcryptHash(storedPasswordHash) { + err = bcrypt.CompareHashAndPassword([]byte(storedPasswordHash), []byte(l.Password)) + if err != nil { + logger.Info("Login: invalid password attempt") + return nil, fmt.Errorf("invalid username/email or password") + } + } else { + // Compare plain text for legacy users + if storedPasswordHash != l.Password { + logger.Info("Login: invalid password attempt (legacy)") + return nil, fmt.Errorf("invalid username/email or password") + } + + // Rehash and update DB for this user + newHash, err := bcrypt.GenerateFromPassword([]byte(l.Password), bcrypt.DefaultCost) + if err == nil { + _, _ = db.Exec(ctx, "UPDATE users SET password = $1 WHERE id = $2", string(newHash), id) + logger.Info(fmt.Sprintf("Upgraded password hash for user %v", id)) + } + } + + // Password verified, get user details user, err := dbutil.UserById(ctx, id.String(), db) if err != nil { logger.Error(fmt.Sprintf("Login: failed to get user by id: %v", err), err) diff --git a/modules/register/verify.go b/modules/register/verify.go index c1f3395..c80cd4e 100644 --- a/modules/register/verify.go +++ b/modules/register/verify.go @@ -7,6 +7,8 @@ import ( "evolve/util" dbutil "evolve/util/db/user" "fmt" + + "golang.org/x/crypto/bcrypt" ) type VerifyReq struct { @@ -63,8 +65,14 @@ func (r *VerifyReq) Verify(ctx context.Context, user map[string]string) error { return fmt.Errorf("invalid otp") } + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user["password"]), bcrypt.DefaultCost) + if err != nil { + logger.Error(fmt.Sprintf("RegisterVerify: failed to hash password: %v", err), err) + return fmt.Errorf("something went wrong") + } + // Insert user into user table. - if _, err := db.Exec(ctx, "INSERT INTO users (email, userName, fullName, password) VALUES($1, $2, $3, $4)", user["email"], user["userName"], user["fullName"], user["password"]); err != nil { + if _, err := db.Exec(ctx, "INSERT INTO users (email, userName, fullName, password) VALUES($1, $2, $3, $4)", user["email"], user["userName"], user["fullName"], string(hashedPassword)); err != nil { logger.Error(fmt.Sprintf("RegisterVerify: failed to insert into users: %v", err), err) return fmt.Errorf("something went wrong") }