Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using a custom User model with SimpleJWT #492

Closed
obvionaoe opened this issue Dec 13, 2021 · 13 comments
Closed

Using a custom User model with SimpleJWT #492

obvionaoe opened this issue Dec 13, 2021 · 13 comments
Labels

Comments

@obvionaoe
Copy link

Hi, I'm trying to use a custom User model/extension of the default Django auth.User model in my application.

I created the User and Manager like so:

import uuid

from django.contrib.auth.base_user import BaseUserManager
from django.contrib.auth.models import AbstractUser
from django.db import models


class CustomUserManager(BaseUserManager):
    def create_user(self, email, company_name, password=None):
        if not email:
            raise ValueError("User must have an email address.")
        if not company_name:
            raise ValueError("User must have a company name.")

        user = self.model(email=self.normalize_email(email),
                          company_name=company_name,
                          )

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, company_name, password):
        user = self.create_user(email=self.normalize_email(email),
                                company_name=company_name,
                                password=password,
                                )

        user.is_admin = True
        user.is_staff = True
        user.is_superuser = True
        user.save(using=self._db)
        return user


class CustomUser(AbstractUser):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    username = None
    email = models.EmailField(verbose_name="email", max_length=60, unique=True)
    company_name = models.CharField(max_length=30)

    # The following fields are required for every custom User model
    last_login = models.DateTimeField(verbose_name='last login', auto_now=True)
    date_joined = models.DateTimeField(verbose_name='date joined', auto_now_add=True)
    is_admin = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['company_name']

    objects = CustomUserManager()

    def __str__(self):
        return self.email

    def has_perm(self, perm, obj=None):
        return self.is_superuser

    def has_module_perms(self, app_label):
        return True

but when I create users of this model and try to login into my app with them instead of getting a JWT token like before I get a message stating that there are no active users with those credentials...

How can I use SimpleJWT with a custom User model?

Thanks

@Andrew-Chen-Wang
Copy link
Member

Try logging into the admin panel and lemme know if that works. I'm guessing you may not have set AUTH_USER_MODEL in settings

@obvionaoe
Copy link
Author

I have set AUTH_USER_MODEL to my custom model app_name.CustomUser and in the admin panel it doesn't appear under Authentication and Authorization anymore. And even if I register it admin.site.register(get_user_model()) it appears under app_name and not under Auth, I believe that might be the issue

@obvionaoe
Copy link
Author

PS I login to the admin panel using a superuser created with python manage.py createsuperuser that asks for the correct fields for my custom user and actually creates a user that is active, but that might be because superuser overrides the is_active flag.

@Andrew-Chen-Wang
Copy link
Member

I have set AUTH_USER_MODEL to my custom model app_name.CustomUser and in the admin panel it doesn't appear under Authentication and Authorization anymore. And even if I register it admin.site.register(get_user_model()) it appears under app_name and not under Auth, I believe that might be the issue

Correct, it should now appear under your project's app which houses the custom user model. This is the proper functionality, and now SImpleJWT should work.

@obvionaoe
Copy link
Author

but it doesn't 😅 I don't know what I'm doing wrong

@Andrew-Chen-Wang
Copy link
Member

haha sorry! Yes please check if is_active=True because we do have a(n overridable) check in place for that. The admin has nothing to do with this package; it was just a test to see if settings.AUTH_USER_MODEL was actually done correctly. Do you mind setting up a reproducible repo? That would help!

@obvionaoe
Copy link
Author

Hey, I created a repo with the files required to reproduce the issue!

@Andrew-Chen-Wang
Copy link
Member

The repo you provided works perfectly fine for me (though I changed database to sqlite3; this should not have any effect).

Steps I've taken:

python manage.py migrate
python manage.py createsuperuser
# Go to http://127.0.0.1:8000/token/ and post my credentials
# Received tokens.

Screen Shot 2021-12-15 at 12 35 07 PM

@obvionaoe
Copy link
Author

have you tried with an user created from the admin panel? please read the readme in the repo

@Andrew-Chen-Wang
Copy link
Member

@obvionaoe ah my apologies. The problem is in your custom manager, you're not setting the password correctly:

>>> from app.models import CustomUser
>>> CustomUser.objects.all()
<QuerySet [<CustomUser: asdf@asdf.com>, <CustomUser: asdf1@asdf.com>]>
>>> CustomUser.objects.all()[1].password
'asdf'

The password should look like this: pbkdf2_sha256$260000$M1oIrwmo8uPU44Rt5vuy6X$lkVHYCCm3U+4FVzSUs1m0zGMUxP/lS8I1cnkCX8il8A=

@Andrew-Chen-Wang
Copy link
Member

Andrew-Chen-Wang commented Dec 15, 2021

This is probably what you're looking for: https://stackoverflow.com/a/69594199

This tutorial can prob help as well: https://testdriven.io/blog/django-custom-user-model/#admin

@Andrew-Chen-Wang
Copy link
Member

Closing.

Reasoning: devs when using a custom user model need to ensure that when they are registering their models, they continue to use the UserAdmin:

from django.contrib.auth.admin import UserAdmin
from django.contrib import admin
from app.models import CustomUser

# Inherit from UserAdmin and do more stuff
# Tutorial: https://testdriven.io/blog/django-custom-user-model/#admin

admin.site.register(CustomUser, UserAdmin)

@Alex3917
Copy link

Alex3917 commented Oct 18, 2022

I ran into this also, so I wanted to document what was going wrong. Basically what is happening is that the error message is incorrect, and has nothing to do with custom user models. The actual root cause is circular imports, which just so happens to cause Django to fail on fetching the custom user model.

In my specific case, I was using a custom serializer to add some information into the token claims, as shown here: https://django-rest-framework-simplejwt.readthedocs.io/en/latest/customizing_token_claims.html

However in the same file I was also importing some other stuff from rest_framework_simplejwt, e.g. from rest_framework_simplejwt.authentication import JWTAuthentication.

So when this line is executed: from rest_framework_simplejwt.views import TokenObtainPairView

It resulting in a circular import. But instead of just giving a circular import error, it was complaining about the user model not being able to be found. Anyway sorry this doesn't come with a neat reproduction, but hopefully this hint at least helps someone else!

(The solution btw was just to define CustomTokenObtainPairView in a file with other views already in it, rather than in the file where other rest_framework_simplejwt were being subclassed.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants