elijaveto
elijaveto

Reputation: 31

check password always returns false django

I work with django and store user passwords as hashed values created with make_password. When I run check_password, I always get a false return and I am out of ideas how to solve this.

Any help would be much appreciated.

My user registration looks like this:

def user_reg_view(request):
    form = UserForm(request.POST or None)
    if form.is_valid():
        password = hashers.make_password(form.cleaned_data.get('password'))
        fname = form.cleaned_data.get('first_name')
        lname = form.cleaned_data.get('last_name')
        email = form.cleaned_data.get('email')
        company_id = form.cleaned_data.get('company')
        User.objects.create_user(
            email = email,
            password=password,
            first_name = fname,
            last_name = lname,
            username = email,
            company_id = company_id)
        form = UserForm()
    var = {'form': form}
    return render(request, 'user_registry.html', var)

And my login function part that fails looks like so (assume the user exists and password entered is always the same):

def login_view(request):
    form = LoginForm(request.POST or None)
    if form.is_valid():

        username = form.cleaned_data.get('username')
        user = User.objects.get(username=username)
        password = form.cleaned_data.get('password')
        encoded_password =  user.password
        print(hashers.make_password(password) == user.password)
        #returns false
        print(hashers.check_password(password=password, encoded=hashers.make_password(password), setter=None))
        #returns true
        print(hashers.check_password(password=password, encoded=encoded_password))
        # returns false

I do not get how the first print differs from the second, of course the password hash generated differs each time for the same string but shouldn't check_password be able to process that?

In case the error might be in the register form values passed, here's also a snippet of that function:

class UserForm(forms.ModelForm):
    company = forms.CharField(max_length=50, label='', widget=forms.TextInput(attrs={'placeholder': 'your company'}))
    first_name = forms.CharField(max_length=30, label='', widget=forms.TextInput(attrs={'placeholder': 'first name'}))
    last_name = forms.CharField(max_length=30, label='', widget=forms.TextInput(attrs={'placeholder': 'last name'}))
    email = forms.CharField(max_length=40, label='', widget=forms.TextInput(attrs={'placeholder': 'email'}))
    password1 = forms.CharField(
        strip=False,
        widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}),
        validators=[validate_password],
        label='password')
    password2 = forms.CharField(
        label= 'please confirm password',
        widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}),
        strip=False
    )

    class Meta:
        model = User
        fields = ['first_name', 'last_name', 'email']
        
    def clean_password2(self):
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError('password_mismatch')
        return password2

Upvotes: 1

Views: 919

Answers (3)

Monata
Monata

Reputation: 318

Django is hashing the value you provided again when you call create_user(), you can check out this answer, where it says that you can first use create() and then save() the password as it is, without hashing the value you provide.

Upvotes: 1

elijaveto
elijaveto

Reputation: 31

Double hashing was one of the problems, another one was that I was unknowingly hashing a None value (instead of the actual entered password).

In my registration form (User Form) I work with password1 and password2, after matching them I return password2. In my user_reg_view where I create the user, however, I was setting the password like so:

password = form.cleaned_data.get('password')

This should have been password2 or password1, password was set to None and did not throw an error.

Upvotes: 0

Abdul Aziz Barkat
Abdul Aziz Barkat

Reputation: 21812

The create_user method hashes the password that is passed to it as an argument. Meaning you are hashing the plain text password two times before storing it to the database. Furthermore no need to use hashers.check_password simply use the authenticate function - Django docs

So your registration view would be:

def user_reg_view(request):
    form = UserForm(request.POST or None)
    if form.is_valid():
        password = form.cleaned_data.get('password')
        fname = form.cleaned_data.get('first_name')
        lname = form.cleaned_data.get('last_name')
        email = form.cleaned_data.get('email')
        company_id = form.cleaned_data.get('company')
        User.objects.create_user(
            email = email,
            password=password,
            first_name = fname,
            last_name = lname,
            username = email,
            company_id = company_id)
        form = UserForm()
    var = {'form': form}
    return render(request, 'user_registry.html', var)

And your login view:

from django.contrib.auth import authenticate, login


def login_view(request):
    form = LoginForm(request.POST or None)
    if form.is_valid():

        username = form.cleaned_data.get('username')
        password = form.cleaned_data.get('password')
        user = authenticate(username=username, password=password)
        if user is not None:
            login(request, user) # login if valid credentials
    # other code

Upvotes: 1

Related Questions