Search code examples
pythonsqliteflaskflask-sqlalchemypasslib

Using passlib can register the password but can't verify it again getting error


I am using passlib to store the password in my sqlite database. I am not getting error while storing the new password(registration). But when I try to login with the same user I am getting this error 'TypeError: hash must be unicode or bytes, not sqlalchemy.orm.attributes.InstrumentedAttribute'.

My script

**models.py**
class Login(db.Model,UserMixin):
    "Creates username and password"
    id = db.Column(db.Integer,primary_key=True,nullable=False)
    username = db.Column(db.String,nullable=False)
    password = db.Column(db.String,nullable=False)
    email = db.Column(db.String,nullable=False)

    def __repr__(self):
        return f"Login('{self.id}','{self.username}','{self.password}','{self.email}')"

**routes.py**
from myfolder.security import encrypt_password,check_encrypted_password
def login():
   
    if request.method == 'POST':
        username = request.form.get('username')
        password1 = request.form.get('password')
        data = {'username':username}
        #fetch the email id of the user whose logged in
        user_email_id = Login.query.filter(Login.username==username).values(Login.email,Login.password)
        for logged_user in user_email_id:
            logged_email_id = logged_user.email
            hashed = logged_user.password
        session['logged_user'] = logged_email_id
        completion = validate(username)
        if completion ==False:
            error = 'error.'
        else:
            password_check = check_encrypted_password(password1,Login.password)
            if password_check ==False:
                error = 'error.'
            else:
                user = Login()
                user.name=username
                user.password=password
                login_user(user)
                error = 'Success'
        api_response = {'data':data,'error':error}
        return jsonify(api_response)

I have created new file called security.py

from passlib.context import CryptContext

pwd_context = CryptContext(
    schemes=["pbkdf2_sha256"],
    default="pbkdf2_sha256",
    pbkdf2_sha256__default_rounds=30000
)


def encrypt_password(password):
    return pwd_context.encrypt(password)


def check_encrypted_password(password, hashed):
    return pwd_context.verify(password, hashed)

In the login method I tried different ways but nothing is working.I tried passing the password here. password_check = check_encrypted_password(password1,password) But I am getting this error

raise ValueError("hash could not be identified")
ValueError: hash could not be identified

How should I verify my password and login?


Solution

  • There is a bug in your code. The line

    password_check = check_encrypted_password(password1,Login.password)
    

    should be

    password_check = check_encrypted_password(password1,hashed)
    

    Instead of passing in the hashed password from the database, you are currently passing in the sqlalchemy column definition for the password.

    There are number of other errors in you code that you should be aware of.

    1. Most of the time you use "password1", but there is also one instance of "password".
    2. If a username is provided that is not in the database, both "logged_email_id" and "hashed" variables would not be defined.

    So I would suggest to refactor you code to this:

    from myfolder.security import encrypt_password,check_encrypted_password
    def login():    
    
        if request.method == 'POST':
            username = request.form.get('username')
            password = request.form.get('password')
            data = {'username':username}
            #fetch the email id of the user whose logged in
            logged_user = Login.query.filter(Login.username==username).values(Login.email,Login.password).one_or_none()
            if not logged_user:
                error = 'error.'
            else:
                session['logged_user'] = logged_user.email
                completion = validate(username)
                if completion ==False:
                    error = 'error.'
                else:
                    password_check = check_encrypted_password(password,logged_user.password)
                    if password_check ==False:
                        error = 'error.'
                    else:
                        user = Login()
                        user.name=username
                        user.password=password
                        login_user(user)
                        error = 'Success'
            api_response = {'data':data,'error':error}
            return jsonify(api_response)
    

    In this code I have:

    1. Replaced "password1" with "password", to ensure that "user.password=password" does not generated an error because password is not defined.
    2. Replaced the for loop with "one_or_none()". This returns the first value or none, if the username cannot be found.
    3. Check that the value returned from the database query exists, before trying to user the result of the query.