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')