0

I have a basic Django app using Django rest framework, a custom user model inheriting from the default, and basic token authentication. I am able to generate a token for any user by running python3 ./manage.py drf_create_token {USERNAME/EMAIL} and authenticate relevant requests with it. However, when I attempt to hit my token endpoint (in this case http://127.0.0.1:8000/api/token/, or the equivalent for the deployed app), I get

{
    "non_field_errors": [
        "Unable to log in with provided credentials."
    ]
}

My request body (application/json) is like:

{
    "username": "test@test.com",
    "password": "test"
}

I have ensured that the user is active; that the password is correct; and that the username is the same as the email.

My custom user class:

class Customer(AbstractUser):
    first_name = models.CharField(max_length=240)
    last_name = models.CharField(max_length=240)
    email = models.EmailField(unique=True)
    phone_number = PhoneNumberField(unique=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now_add=True)
    is_active=models.BooleanField(default=True)
    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["password", "phone_number", "first_name", "last_name", "username"]

    def __str__(self):
        return f"{self.first_name} {self.last_name}"

relevant urls:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/users/', include('customer.urls')),
    path('api/token/', obtain_auth_token, name="auth_token"),
]

in settings.py I have the following:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_extensions',
    'phonenumber_field',
    'customer',
    'contact',
    'rest_framework',
    'rest_framework.authtoken',
]
...
REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
    ],
}
...
AUTH_USER_MODEL = 'customer.Customer'
...

My Serializer (here contacts is another model):

class CustomerSerializer(serializers.ModelSerializer):

    contacts = serializers.SerializerMethodField()

    def get_contacts(self, obj):
        from contact.serializers import ContactSerializer
        from contact.models import Contact
        return ContactSerializer(Contact.objects.filter(user=obj), many=True, read_only=True).data

    class Meta:
        model = Customer
        fields = [
            'pk', 
            'first_name', 
            'last_name',
            'email', 
            'phone_number', 
            'created_at', 
            'updated_at',
            'contacts',
        ]

What could I be missing? I have already tried all the solutions here: DRF auth_token: "non_field_errors": [ "Unable to log in with provided credentials."

jeanmw
  • 446
  • 2
  • 17

1 Answers1

1

I found the issue - following https://docs.djangoproject.com/en/4.0/topics/auth/customizing/#a-full-example I was able to see that with a custom user model, you must update the way you are creating those custom users (via admin, auth, or otherwise) to ensure the password is hashed correctly. Following this, I created a custom admin form and a custom user manager that correctly overrides and handles the saving of the model:

    def create_user(self, email, first_name, last_name, phone_number, password=None):
        """
        Creates and saves a User with the given email and password.
        """
        if not email:
            raise ValueError('Users must have an email address')

        user = self.model(
            email=self.normalize_email(email),
            first_name=first_name,
            last_name=last_name,
            phone_number=phone_number,
        )

        user.set_password(password)
        user.save(using=self._db)
        return user
jeanmw
  • 446
  • 2
  • 17