2

I am trying to use token authentication, but it is not working due to my create user serializer not hashing the passwords. I am able to login with the superuser as that has a hashed password. Using rest_auth and rest_framework.authtoken. The user.set_password command is supposed to hash the password, so is there an issue with the prior code?

class CreateUserSerializer(serializers.HyperlinkedModelSerializer):
    username = serializers.CharField()
    password = serializers.CharField(write_only = True, style = {'input_type': 'password'})

    class Meta:
        model = get_user_model()
        fields = (
            'id','username', 'password', 
            'email', 'first_name', 'last_name'
        )
        write_only_fields = ('password')
        read_only_fields = ('is_staff', 'is_superuser', 'is_active')

        def create(self, validated_data):
            password = validated_data.pop('password')
            user = super().create(validated_data)
            user.set_password(validated_data['password'])
            user.save()
            return user
class CreateUserAPIView(CreateAPIView):
    """
    Create a new user.
    """
    serializer_class = CreateUserSerializer
    permission_classes = [AllowAny]

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data = request.data)
        serializer.is_valid(raise_exception = True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)

        # Create a token that will be used for future auth
        token = Token.objects.create(user = serializer.instance)
        token_data = {"token": token.key}

        return Response(
            {**serializer.data, **token_data},
            status = status.HTTP_201_CREATED,
            headers = headers
        )
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 20,

    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
    ),

    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    )
}
class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = (
            'url', 'username', 'email', 'groups', 'workflow_step',
            'first_name', 'last_name', 
            'birthdate',
            'address_street1', 'address_street2', 'address_city', 
            'address_state', 'address_postal_code', 'address_country', 'phone'
        )
class User(AbstractUser):
    # Application process
    workflow_step = models.CharField(max_length=100, default='', blank=True)

    is_verified = models.BooleanField(default=False)

    # Basic information
    # first_name (in django.contrib.auth.models.User)
    # last_name (in django.contrib.auth.models.User)
    # email (in django.contrib.auth.models.User)

    # Advanced Information
    birthdate = models.DateField(blank=True, null=True)
    address_street1 = models.CharField(max_length=100, blank=True)
    address_street2 = models.CharField(max_length=100, default='', blank=True)
    address_city = models.CharField(max_length=100, blank=True)
    address_state = models.CharField(max_length=50, blank=True)
    address_postal_code = models.CharField(max_length=30, blank=True)
    address_country = models.CharField(max_length=100, blank=True)
    phone = models.CharField(max_length=30, blank=True)

6 Answers 6

11

This is probably too late, but for anyone who has this issue. you need to put the create function directly inside the serializer class, in your case you have it in the Meta subclass

The second thing you need to do is to use

def create(self, validated_data):
    password = validated_data.pop('password')
    user = super().create(validated_data)
    user.set_password(password)
    user.save()
    return user

best of luck

Sign up to request clarification or add additional context in comments.

Comments

1

This works for me..try this

class UserSerializer(serializers.ModelSerializer):
    # <Your other UserSerializer stuff here>

    def create(self, validated_data):
        password = validated_data.pop('password', None)
        instance = self.Meta.model(**validated_data)
        if password is not None:
            instance.set_password(password)
        instance.save()
        return instance

Comments

0

In CreateUserSerializer.create you're doing this:

        password = validated_data.pop('password')
        ...
        user.set_password(validated_data['password'])

By the time you call set_password the passwordkey has been removed from validated_data. You probably want to change the set_password line to this instead:

        user.set_password(password)

6 Comments

That still didn't fix the issue - passwords are still not hashed.
So a new user is created but their password field is empty? Or are you getting an error?
A new user is created, but the password field has the plaintext password and not the hashed password.
Are you using a custom user model?
Yes, I've added the UserSerializer and custom model to the original question as reference
|
0

You can use the make_passowrd function in order of hashing it:

class CreateUserSerializer(serializers.HyperlinkedModelSerializer):
    username = serializers.CharField()
    password = serializers.CharField(write_only = True, style = {'input_type': 'password'})

    class Meta:
        model = get_user_model()
        fields = (
            'id','username', 'password', 
            'email', 'first_name', 'last_name'
        )
        write_only_fields = ('password')
        read_only_fields = ('is_staff', 'is_superuser', 'is_active')

        def create(self, validated_data):
            password = validated_data.pop('password')
            user = super().create(validated_data)
            user.set_password( make_password(validated_data['password']))
            user.save()
            return user

Read all about password managing here

Comments

0

You are removing the 'password' key before hashing it. You need to change user.set_password(validated_data['password']) this to user.set_password(password) as you popped that from validated data and stored to password variable.

Comments

0

Saving hashed password in Serializer using validated_data dict doesnt work, it saves password in plain text even when make_password method is used.

I got this done by altering the value of password filed in view class itself and by using make_password method to hash the password before saving the Serializer...see the code below:

class PostAccountView(generics.ListCreateAPIView):
# Comment the below parser to use this view via Postman, make sure parameters are added as form_data under body section in postman
# parser_classes = [FormParser]

queryset = Registration_Model.objects.all()
serializer_class = PostAccountSerializer

def post(self, request, format=None):
    serializer = PostAccountSerializer(data=request.data)

    if serializer.is_valid():
        serializer.validated_data["password"] = make_password(serializer.validated_data["password"])
        serializer.validated_data["confirm_password"] = make_password(serializer.validated_data["confirm_password"])
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.