# Buggy code
def process_record(self, new, old=None):
    new = super(Account, self).process_record(new, old)

    # Store password safely in database.
    pwd_str = new["password"].encode(encoding="utf-8")
    new["password"] = bcrypt.hashpw(pwd_str, bcrypt.gensalt())

    # Administrators can reach other accounts and anonymous have no
    # selected_userid. So do not try to enforce.
    if self.context.is_administrator or self.context.is_anonymous:
        return new

    # Otherwise, we force the id to match the authenticated username.
    if new[self.model.id_field] != self.request.selected_userid:
        error_details = {
            "name": "data.id",
            "description": "Username and account ID do not match.",
        }
        raise_invalid(self.request, **error_details)

    return new


# Traceback (TypeError)
Unicode-objects must be encoded before hashing
Traceback (most recent call last):
File "/usr/local/lib/python3.5/dist-packages/pyramid/tweens.py", line 22, in excview_tween
response = handler(request)
File "/usr/local/lib/python3.5/dist-packages/pyramid_tm/__init__.py", line 119, in tm_tween
reraise(*exc_info)
File "/usr/local/lib/python3.5/dist-packages/pyramid_tm/compat.py", line 15, in reraise
raise value
File "/usr/local/lib/python3.5/dist-packages/pyramid_tm/__init__.py", line 98, in tm_tween
response = handler(request)
File "/usr/local/lib/python3.5/dist-packages/pyramid/router.py", line 155, in handle_request
view_name
File "/usr/local/lib/python3.5/dist-packages/pyramid/view.py", line 612, in _call_view
response = view_callable(context, request)
File "/usr/local/lib/python3.5/dist-packages/pyramid/config/views.py", line 181, in __call__
return view(context, request)
File "/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py", line 389, in attr_view
return view(context, request)
File "/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py", line 367, in predicate_wrapper
return view(context, request)
File "/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py", line 438, in rendered_view
result = view(context, request)
File "/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py", line 147, in _requestonly_view
response = view(request)
File "/usr/local/lib/python3.5/dist-packages/cornice/service.py", line 493, in wrapper
response = view_(request)
File "/code/kinto/core/views/hello.py", line 45, in get_hello
if Authenticated in request.effective_principals:
File "/usr/local/lib/python3.5/dist-packages/pyramid/security.py", line 375, in effective_principals
return policy.effective_principals(self)
File "/usr/local/lib/python3.5/dist-packages/pyramid_multiauth/__init__.py", line 119, in effective_principals
userid = policy.authenticated_userid(request)
File "/usr/local/lib/python3.5/dist-packages/pyramid/authentication.py", line 92, in authenticated_userid
callback_ok = self.callback(userid, request)
File "/usr/local/lib/python3.5/dist-packages/pyramid/authentication.py", line 1123, in callback
return self.check(username, password, request)
File "/code/kinto/plugins/accounts/authentication.py", line 18, in account_check
if hashed == bcrypt.hashpw(pwd_str, hashed):
File "/usr/local/lib/python3.5/dist-packages/bcrypt/__init__.py", line 62, in hashpw
raise TypeError("Unicode-objects must be encoded before hashing")

# Fixed code
def process_record(self, new, old=None):
    new = super(Account, self).process_record(new, old)

    # Store password safely in database as str
    # (bcrypt.hashpw returns base64 bytes).
    pwd_str = new["password"].encode(encoding="utf-8")
    hashed = bcrypt.hashpw(pwd_str, bcrypt.gensalt())
    new["password"] = hashed.decode(encoding="utf-8")

    # Administrators can reach other accounts and anonymous have no
    # selected_userid. So do not try to enforce.
    if self.context.is_administrator or self.context.is_anonymous:
        return new

    # Otherwise, we force the id to match the authenticated username.
    if new[self.model.id_field] != self.request.selected_userid:
        error_details = {
            "name": "data.id",
            "description": "Username and account ID do not match.",
        }
        raise_invalid(self.request, **error_details)

    return new