Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python

Ken Alger
STAFF
Ken Alger
Treehouse Teacher

[SOLVED] Python, and Flask, and Bcrypt, oh my!

Attempting to verify passwords with Flask and Bcrypt. Cannot use flask.ext.bcrypt because of a lack of a Visual Studio 2010 component on my system, so I am just using bcrypt.

I am saving the password to a database and in doing so am having to encode it otherwise I get a TypeError: Unicode-objects must be encoded before hashing. Code would be similar to:

models.py
import bcrypt
import datetime

from flask.ext.login import UserMixin
from peewee import *
from playhouse.postgres_ext import PostgresqlExtDatabase

DATABASE = PostgresqlExtDatabase(database='leaderboard', user='postgres')
ROUNDS = 5     # Number of hash rounds, set low for development, increase for production

class BaseModel(Model):
    class Meta:
         database = DATABASE

class User(UserMixin, BaseModel):
    email = CharField(unique=True)
    password = CharField(max_length=100)
    joined_at = DateTimeField(default=datetime.datetime.now)

    class Meta:
        order_by = ('joined_at',)

    @classmethod
    def create_user(cls, email, password):
        try:
            with DATABASE.transaction():
                cls.create(
                    email=email,
                    password=bcrypt.hashpw(password.encode('utf-8', bcrypt.gensalt(ROUNDS))
                )
        except IntegrityError:
            raise ValueError("Sorry, user already exists.")


def initialize():
    DATABASE.connect()
    DATABASE.create_tables([User], safe=True)
    DATABASE.close()

That seems to work just fine as long as I include the encode('utf-8').

forms.py
from flask.ext.wtf import Form
from wtforms import StringField, PasswordField
from wtforms.validators import (DataRequired, Email)


class LoginForm(Form):
    """The form for logging into the site"""
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired()])

With me so far?

Now, in my application when I go to check the password in the form against the database I have:

login_route.py
if bcrypt.hashpw(form.password.data, student.password) == student.password:

Wham! Back to my TypeError from before. My question then, and perhaps Kenneth Love would be so kind as to shed some light on the subject, is when and where is bcrypt expecting encoding to occur and, more importantly, how can I verify my passwords?

Any help is very much appreciated.

Ken

3 Answers

Ken Alger
STAFF
Ken Alger
Treehouse Teacher

Sorry for dropping this thread guys, I'll attempt to recall what I did, but if what I post isn't working for you, please bear with me and we can work through it together, okay?

1) I included hashpw from bcrypt:

from bcrypt import hashpw

2) In my route I changed by password check to (tabs not accurate in snippet):

if student.password == hashpw(form.password.data.encode('UTF_8'), 
      student.password.encode('UTF_8')).decode():

If memory serves the way the hashing works with UTF-8 requires those encode() methods and then the decode() pops it back out to be able to do the equality check. Took me quite a bit of searching to figure it out and even longer playing with various orders of encoding and decoding and where to have the UTF-8. Kind of was one of those "Holly cow, that worked!" moments when it finally worked.

Post back with other questions. I'll try to be more responsive than 4 or 5 months. :cry:

Happy coding,
Ken

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 68,423 Points

I was also getting Unicode-objects must be encoded before hashing errors. I was most curious about how thing worked in the Workspace but not locally. I downloaded a workspace and it failed locally. I figured it was an environment problem. bcrypt was not working for me.

In the workspace I ran the command:

$ pip freeze > requirements.txt

Looking at this file, I did not see bcrypt. Instead, there was py-bcrypt! So uninstalled bcrypt and its dependents:

# No longer needed
$ pip uninstall bcripyt
$ pip uninstall cff8
$ pip uninstall pycparcer
# get py-bcrypt
$ pip install py-bcrypt

Then everything worked as expected. No need to add .encode() to passwords, From scratch, I was able to run flask locally using my answer to How to run flask locally

Ken Alger
STAFF
Ken Alger
Treehouse Teacher

Never mind, I was able to figure it out.

What was your solution? I am running into the same problem.

Chris Freeman
Chris Freeman
Treehouse Moderator 68,423 Points

Andrew Hill: Are you trying to run outside of the Workspaces? What environment are you using?

Ken Alger: Were you also trying to work outside of Workspaces? Are you on Windows?

I have been trying to run the examples (as typed by Kenneth, no from workspaces) on Ubuntu 14.04. I may have solved it, but I wanted to check in with both of you on your environments.

Thanks.