Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Django cannot authenticate or password hashing is wrong

I use a custom user model so I can authenticate using email instead of username.

from django.db import models
from django.contrib.auth.models import (
    AbstractBaseUser,
    BaseUserManager,
    PermissionsMixin,
)


class UserManager(BaseUserManager):
    def create_user(
        self,
        email,
        password,
        confirm_code=None,
        username=None,
        role=None,
    ):
        user = self.model(email=self.normalize_email(email))
        user.set_password(password)
        user.confirm_code = confirm_code
        user.save()
        return user

    def create_superuser(self, email, password, role, username=None):
        user = self.model(email=self.normalize_email(email))
        user.set_password(password)
        user.role = role
        user.is_staff = True
        user.is_active = True
        user.is_superuser = True
        user.save()
        return user


class User(AbstractBaseUser, PermissionsMixin):
    EM = "EM"
    SM = "SM"
    DH = "DH"
    ST = "ST"
    ROLES = [
        (EM, "Executive Management"),
        (SM, "Senior Management"),
        (DH, "Department Head"),
        (ST, "Staff Member"),
    ]

    objects = UserManager()
    role = models.CharField(max_length=2, choices=ROLES, default=US, blank=True)
    username = models.CharField(max_length=20, unique=True, blank=True, null=True)
    email = models.EmailField(max_length=255, unique=True)
    slug = models.SlugField(blank=True, null=True)
    confirm_code = models.CharField(max_length=20, null=True, blank=True)
    is_active = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)
    has_profile = models.BooleanField(default=False)
    email_verified_at = models.DateTimeField(auto_now=False, null=True, blank=True)
    code = models.CharField(max_length=8, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created at")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="Updated at")

    class Meta:
        verbose_name = "User"
        verbose_name_plural = "Users"
        ordering = ["username"]
        db_table = "users"

    def get_absolute_url(self):
        return f"{self.slug}"

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["role"]

When I register a user the user is created in the database correctly and the password is hashed I assume correctly, because I see the hash and not the raw password. Here is the method I used to register a user with:

@api_view(["POST"])
def RegisterUser(request, *args, **kwargs):
    code = []
    numbers = range(7)
    for num in numbers:
        code.append(randint(0, 9))

    email = request.data["email"]
    confirmemail = request.data["confirmemail"]
    password = request.data["password"]
    confirmpassword = request.data["confirmpassword"]
    errors = []
    if email != confirmemail:
        errors.append("emailmatch")
        return Response("Emails do not match", status=500)
    if confirmpassword != password:
        errors.append("passmatch")
        return Response("Password do not match", status=500)
    if User.objects.filter(email=email):
        errors.append("emailexists")
        return Response("User is already registered", status=500)
    else:
        pass
    if len(errors) > 0:
        return Response(False)
    else:
        password_to_save = make_password(password)
        confirm_code = "".join(str(e) for e in code)
        user = User.objects.create_user(
            email=email, password=password_to_save, confirm_code=confirm_code
        )
        token = Token.objects.create(user=user)
        from_email = "info@website.com"
        link = f"http://127.0.0.1:8000/api/v1/users/confirm/{user.id}/{token}/"
        context = {"link": link, "code": code}
        template = get_template("emails/welcome.html").render(context)

        subject = "Successfully registered"
        message = "Welcome to website"

        try:
            send_mail(
                subject,
                message=message,
                from_email=from_email,
                recipient_list=[email],
                html_message=template,
            )
        except:
            return Response("Could not send mail")

        serializer = RegisterSerializer(user)

        return Response(serializer.data)

I have a custom backend to authenticate a user using an email instead of a username and here that is:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

class EmailBackend(ModelBackend):
    def authenticate(self, request, **kwargs):
        email = kwargs.get("email", None)
        user = User.objects.get(email=email)
        if user:
            breakpoint()
            if user.check_password(kwargs.get("password", None)):
                return user
            else:
                return None
        else:
            User.DoesNotExist
        return None

It just doesn’t work and I cannot log in. The user is active! If I do a breakpoint() as seen here and I check the password manually by entering the raw password in user.check_password(‘password’) then it returns false. So isn’t check_password supposed to then hash the raw password entered and compare that hash with the hash in the database or how does it work?

This works:

password = "hello"
saved = make_password(password)
true = check_password(password, saved)

So how why does it not work in my Authenticate backend which is exactly the same?

Do I need to use another hashing algorithm or what?

What am I supposed to do then and how can I authenticate a user? Is there another way to compare the password a user entered on a form and the hashed password saved in the database?

>Solution :

You are using set_password function in create_user in your manager. This method itself calls make_password function.

So, when you pass a hashed password generated via make_password to create_user, that hashed password would be hashed again in set_password function.

So there is no need to call make_password by yourself. Just pass plain password to create_user method.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading