Ryan Skene
Ryan Skene

Reputation: 924

flask-bcrypt Invalid Salt; encoding issue?

I know a similar question has been asked here, but no answer seems to address my problem. I am using Python3, Flask, and flask-bcrypt.

My login function was working fine earlier and I don't believe I changed anything so I'm not sure what happened. The way I understand it is, at signup, we are meant to encode the received password to UTF-8, then decode the hashed password before we pass it to the database as follows:

password = form.password.data.encode('utf-8')
hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')

Then, at login, we are supposed to encode the candidate password and compare that to the hashed password in the database as follows:

password = form.password.data.encode('utf-8')
if bcrypt.check_password_hash(user.password, password):

But i get the error Invalid Salt from this. And I have tried several other variants with no success.

Any help would be greatly appreciated. Here are the views. I can provide any other code necessary.

@app.route('/<role>/signup/', methods=['GET', 'POST'])
def signUp(role):
    form = RegisterForm()

    if form.validate_on_submit():
        password = form.password.data.encode('utf-8')
        hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')
        new_user = User(role=role, password=hashed_password, visits=1)
        form.populate_obj(new_user)

        db.session.add(new_user)
        db.session.commit()

        login_user(new_user)

        return redirect(url_for('createContactInfo', user_id=current_user.id))

    return render_template('signup.html', form=form, role=role)

@app.route('/login/', methods=['GET', 'POST'])
def logIn():
    form = LoginForm()

    if form.validate_on_submit():
        password = form.password.data.encode('utf-8')
        user = User.query.filter_by(email=form.email.data).first()
        if user:
            if bcrypt.check_password_hash(user.password, password):
                login_user(user, remember=form.remember.data)
                User.query.filter_by(id=current_user.id).update({'visits': User.visits + 1})
                db.session.commit()

                if current_user.role == 'admin':
                    return redirect(url_for('adminMainPage', user_id=current_user.id))
                elif current_user.role == 'guest':
                    return redirect(url_for('guestMainPage', user_id=current_user.id))
                else:
                    return "wrong role!"

            flash('Invalid login')
            return render_template('login.html', form=form)

        flash('Invalid login')
        return render_template('login.html', form=form)

    return render_template('login.html', form=form)

Upvotes: 1

Views: 2334

Answers (1)

Ryan Skene
Ryan Skene

Reputation: 924

So I figured it out and the answer really had nothing to do with the question.

The issue was, in signup, I instantiated the object new_user with password=hashed_password, but then I called form.populate_obj(new_user), which according to the wtforms docs is a destructive operation, and as a result overrode new_user.password = form.password.data, which was not hashed.

So I stored the unhashed password in my DB and, at login, I was comparing the hashed candidate password with the unhashed stored password. Hence, Invalid Salt. As discussed in other related matters, the Invalid Salt error is a huge catchall that doesn't really point to the underlying problem.

To fix, I called form.populate_obj(new_user) first, then added the attributes separately as follows:

    new_user = User()

    form.populate_obj(new_user)

    new_user.role = role
    new_user.password = hashed_password
    new_user.visits = 1

Upvotes: 1

Related Questions