maxcountryman / flask-bcrypt

Flask-Bcrypt is a Flask extension that provides bcrypt hashing utilities for your application.

Home Page:http://readthedocs.org/docs/flask-bcrypt/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Works in Python2 but not in Python (Invalid Salt)

Cabalist opened this issue · comments

Hey there,

I am getting some odd behaviour in Python3.

My code looks like this:

default_user = User(name="John Doe")
default_user.set_password("password")
assert default_user.check_password("password") is True
assert default_user.check_password("kldsjfsakdjf") is False

set_password is defined as:

    def set_password(self, password):
        self.password = bcrypt.generate_password_hash(password)
    def check_password(self, value):
        # Users without password can't log in
        # we prevent to hash an empty string
        if not value and not self.password:
            return False
        return bcrypt.check_password_hash(self.password, value)

This code works great in Python2. When I run the same code in Python3 I get the "Invalid Salt" error.

Investigating it looks like instead of the hash being written to my db (Postgres) I get a different string.

For example in Python 3 when I look at User.password in the db I see:

\x24326224313324674b644e614e5570476d347143502e6539735063704f4e392e6a2e5956714b726a537575312e354e4c4971505761464e3771423843

instead of

b'$2b$13$gKdNaNUpGm4qCP.e9sPcpON9.j.YVqKrjSuu1.5NLIqPWaFN7qB8C'

which is the value before I save the user and the one I should receive when I check it.

This looks like some kind of encoding error but I can't pinpoint what it is doing it! Any suggestions would be greatly appreciated. 😄

Alright. I have an answer. I am not sure it is the right answer though...

    def set_password(self, password):
        self.password = bcrypt.generate_password_hash(password)

should be

    def set_password(self, password):
        self.password = bcrypt.generate_password_hash(password).decode('utf-8')

This fixed my problem. Is this the correct place to do that?

I'm afraid I'm not much of a Python 3 expert, but I believe strings are Unicode, so if your driver is not properly handling the conversion for you then you will in fact have to decode their UTF-8 bytes. Maybe there's a better way of doing things at the library level that's more idiomatic to Python 3 but unfortunately I don't use Python 3 and can't really comment.

Yep. That was the correct place to put it. SQLAlchemy was not expecting a bytestring which is what generate_password_hash() returns. I explicitly set it to a Unicode String and SqlAlchemy handles the rest.

You can consider this closed. :)

This needs to be in documentation for python 3 users:

def set_password(self, password):
    self.password = bcrypt.generate_password_hash(password).decode('utf-8')