Authentication only working with superuser, unable to log in regular users, passwords not hashed

I’m not sure, but it may be because the passwords aren’t hashed for the regular users, only the user created by the create superuser command. I’ve designated some other users as superusers, but they can’t log in either, so the only remaining variable seems to be the lack of password hashing. However, I can’t figure out how to get the passwords hashed when creating new users. I am logging into the admin site and creating users but their passwords show as plaintext.

views.py:
class CustomUser(viewsets.ModelViewSet):
serializer_class = UserSerializer
queryset = CustomUserModel.objects.all()

models.py:
class CustomUser(AbstractUser):
ACCOUNT_TYPE = [(‘1’, ‘Student’), (‘2’, ‘Teacher’)]
accountType = models.CharField(max_length=1, choices=ACCOUNT_TYPE, default=‘1’)
classCode = models.CharField(max_length=30, blank=True)
objects = CustomUserManager() # new
def str(self):
return self.email

I have also attempted to add a CustomUserManager in models.py but I don’t see any evidence that it runs when I create a new user account:
class CustomUserManager(BaseUserManager):
def create_user(self, email, accountType, classCode, password):
if not email:
raise ValueError(‘Users must have an email address’)
user = self.model(
email=self.normalize_email(email),
accountType=accountType,
classCode=classCode,
)
user.save(using=self._db)
return user

Is there any way to make sure passwords are saved hashed with a custom user?

That is not correct.

If you’re creating your users appropriately, passwords are always hashed.

If you look at the Django Admin forms for creating a new user, you’ll see that the form contains two fields - password1 and password2. Those two fields are compared to each other, and if they match, one of them is hashed and stored into the password field.
(What you don’t want to do is just store the password entered into the form into the password field of the user object.)

The set_password method on the user is used to set the password field to the appropriately hashed value.

Side note: When posting code here, you want to surround the code with lines of three backtick - ` characters. This means you’ll have a line of ```, then your code, then another line of ```. (Make sure the lines of ``` are on lines by themselves and that you use the backtick character - ` and not the apostrophe - '.)

Thanks for your help! I will use the backtick characters in the future. The behavior I’m seeing is that on the Django administration page, in the Users section, if I click on ADD USER there’s a form asking for a Password, Username, and some other fields including Active checkbox, First name, Last name, etc. There’s only one password field on this form. When I save the form, then return to check on that user account, the Password field at the top is not hashed (only the superuser created with the superuser command from the command line is hashed). My front end application cannot log into that new account either, but authentication with superuser credentials works. I’m wondering if there’s something wrong with my CustomUser model, or my view that doesn’t store the hashed password, and where I should call set_password?

I see you’re using a CustomUser model. Have you defined a custom CustomUserAdmin class? If you haven’t, you need to - and you want it to inherit from UserAdmin, not the standard ModelAdmin class.

(I suggest you look at django.contrib.auth.admin.UserAdmin to see everything it’s doing. Among other things, it considers adding a new user to be a different operation than editing an existing user - it’s all in the admin class.)

Depending upon what extra fields you are adding to your CustomUser, you may not need to do anything special in your CustomUserAdmin class - that’s where I would start anyway.

You are correct - authentication will not work with an unhashed password.

Hi, thanks for your help. I’ve tried adding a CustomUserAdmin class and that does work to hash passwords when creating users from the django admin site. However, it broke my front end. My user creation form is on the front end and sends the username, password, etc. to the back end in a json object via a ajax post request. Is there a location to insert the set_password code so that the password is hashed after the data is received from the front end, and the hashed password is saved in the database?

Please paste your view that handles the ajax post. It’s going to be a lot easier to show this in the context of your actual code.

(In general, you create the User object, call set_password on that object, and then save it.)

Thanks! Right now I have this view:

@method_decorator(ensure_csrf_cookie, name='dispatch')
class CustomUser(viewsets.ModelViewSet):
    serializer_class = UserSerializer
    queryset = CustomUserModel.objects.all()

Ahhhh… you’re using DRF.

First the caveats - I only have a very superficial knowledge of DRF, and I’m not aware of any specific features in DRF to facilitate the creation of a user.

Having said that - a quick google search for creating a user with DRF reveals that one of our regulars here has written a blog post that appears to be on-point. See How to create/register user account with Django Rest Framework API | Filip Němeček

1 Like

Yay, thank you so much for sending this!!! It finally works now