How to compare two passwords properly? Bcrypt

Hello!

I’m trying to verify if the user entered valid credentials, so far, I’m storing the hashed password in my users table along with other fields (I’m using Gorm).

The problem comes when I try to validate the password as I’m not sure how to approach it, my first thought was receiving JSON and decoding it into my User structure, then, find an user with the ID provided (instead of say, email - as of now) and store it in another variable of type User, then use bcrypt.CompareHashAndPassword to compare the passwords but it’s returning nil (valid match) every single time even if the passwords are not the same.

Here is my handler:

func Login(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	db := utils.InitDB()
	defer db.Close()

	// This is the "user" that comes along with the POST request
	var user models.User
	decoder := json.NewDecoder(r.Body)
	err := decoder.Decode(&user)
	if err != nil {
		utils.ErrorWithJSON(w, "Invalid data", http.StatusBadRequest)
		return
	}

	// This is the user found in the database with the ID provided in the POST request
	var u2 models.User
	id := user.ID
	db.First(&u2, id)
	if u2.ID == 0 {
		utils.ErrorWithJSON(w, "User not found", http.StatusNotFound)
		return
	}

	err = bcrypt.CompareHashAndPassword([]byte(u2.Password), []byte(user.Password))
	if err != nil {
		log.Fatal("WRONG PASSWORD")
		return
	}
	log.Println("CORRECT PASSWORD")
}

A POST request would be similar to (as of now).

{
	"id": 2,
	"name": "Andres",
	"password": "123456"
}

I’m pretty sure my approach is not the correct one, so I would like to know why is it failing and what would be a better way to do it?

Thank you so much for your time!

EDIT

The problem was that I had my Password field being ignored in the structure

type User struct {
	ID        uint       `gorm:"primary_key" json:"id"`
	CreatedAt time.Time  `json:"created_at"`
	UpdatedAt time.Time  `json:"updated_at"`
	DeletedAt *time.Time `json:"deleted_at,omitempty"`

	Name     string `gorm:"type:varchar(100); not null" json:"name"`
	Username string `gorm:"type:varchar(30); unique_index; not null" json:"username"`
	Email    string `gorm:"type:varchar(100); unique_index; not null" json:"email"`
	Country  string `gorm:"type:varchar(30); not null" json:"country,omitempty"`
	Password string `gorm:"type:varchar(255); not null" json:"-"`

	CohortID uint `json:"cohort_id,omitempty"`
}

It works correctly now, however, I do not want to send my Password in future GET requests, how could I avoid this then?

Any suggestions to improve the code would be super appreciated, once again, thanks for your time.

You could have two fields on your user struct. Something like this:

Password     string `gorm:"-"`
PasswordHash string  `gorm:"type:varchar(255); not null"`

Then compare the PasswordHash you have stored in your DB to the password passed by the user (which is never saved on the DB).

1 Like

Assuming you mean that you have your struct and that you want to be able to serialize it both with and without the password field (which is of course the password hash)… You could use a separate struct that is identical but has different tags, for example:

type User struct {
    // ...
	Password string `gorm:"type:varchar(255); not null"`
}

type userNoPasswordJSON struct {
    // ...
	Password string `json:"-"`
}

var u User
// ...
bs, err := json.Marshal(userNoPasswordJSON(u))

Or you could simply set it to empty or a sentinel value before serializing.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.