Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update user password in Django Rest Framework?

I want to ask that following code provides updating password but I want to update password after current password confirmation process. So what should I add for it? Thank you.

class UserPasswordSerializer(ModelSerializer):

    class Meta:
        model = User
        fields = [
            'password'
        ]

        extra_kwargs = {
            "password": {"write_only": True},
        }

    def update(self, instance, validated_data):
        for attr, value in validated_data.items():
            if attr == 'password':
                instance.set_password(value)
            else:
                setattr(instance, attr, value)
        instance.save()
        return instance
like image 755
bysucpmeti Avatar asked Aug 09 '16 07:08

bysucpmeti


People also ask

How can I see my password in Django?

In order to use the built-in Django check_password() function, we need to import it, which is shown in the first line of code. So the current password of the user is, request. user. password, which we store in the currentpassword variable.

How does Django encrypt passwords?

By default, Django uses the PBKDF2 algorithm with a SHA256 hash, a password stretching mechanism recommended by NIST. This should be sufficient for most users: it's quite secure, requiring massive amounts of computing time to break.


3 Answers

I believe that using a modelserializer might be an overkill. This simple serializer & view should work.

Serializers.py

from rest_framework import serializers from django.contrib.auth.models import User  class ChangePasswordSerializer(serializers.Serializer):     model = User      """     Serializer for password change endpoint.     """     old_password = serializers.CharField(required=True)     new_password = serializers.CharField(required=True) 

Views.py

from rest_framework import status from rest_framework import generics from rest_framework.response import Response from django.contrib.auth.models import User from . import serializers from rest_framework.permissions import IsAuthenticated     class ChangePasswordView(UpdateAPIView):         """         An endpoint for changing password.         """         serializer_class = ChangePasswordSerializer         model = User         permission_classes = (IsAuthenticated,)          def get_object(self, queryset=None):             obj = self.request.user             return obj          def update(self, request, *args, **kwargs):             self.object = self.get_object()             serializer = self.get_serializer(data=request.data)              if serializer.is_valid():                 # Check old password                 if not self.object.check_password(serializer.data.get("old_password")):                     return Response({"old_password": ["Wrong password."]}, status=status.HTTP_400_BAD_REQUEST)                 # set_password also hashes the password that the user will get                 self.object.set_password(serializer.data.get("new_password"))                 self.object.save()                 response = {                     'status': 'success',                     'code': status.HTTP_200_OK,                     'message': 'Password updated successfully',                     'data': []                 }                  return Response(response)              return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 
like image 57
Yiğit Güler Avatar answered Sep 23 '22 06:09

Yiğit Güler


@Yiğit Güler give a good answer, thanks, but it could be better in some minor points.

As long you don't really works with UpdateModelMixin, but directly with the request user instance, you don't need to use a UpdateAPIView. A simple APIView is enough.

Also, when the password is changed, you can return a status.HTTP_204_NO_CONTENT instead of a 200 with some random content.

By the way, don't forgot to validate your new password before save. It's too bad if you allow "password" at update while you don't at create.

So I use the following code in my project:

from django.contrib.auth.password_validation import validate_password  class ChangePasswordSerializer(serializers.Serializer):     """     Serializer for password change endpoint.     """     old_password = serializers.CharField(required=True)     new_password = serializers.CharField(required=True)      def validate_new_password(self, value):         validate_password(value)         return value 

And for the view:

class UpdatePassword(APIView):     """     An endpoint for changing password.     """     permission_classes = (permissions.IsAuthenticated, )      def get_object(self, queryset=None):         return self.request.user      def put(self, request, *args, **kwargs):         self.object = self.get_object()         serializer = ChangePasswordSerializer(data=request.data)          if serializer.is_valid():             # Check old password             old_password = serializer.data.get("old_password")             if not self.object.check_password(old_password):                 return Response({"old_password": ["Wrong password."]},                                  status=status.HTTP_400_BAD_REQUEST)             # set_password also hashes the password that the user will get             self.object.set_password(serializer.data.get("new_password"))             self.object.save()             return Response(status=status.HTTP_204_NO_CONTENT)          return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 
like image 28
tominardi Avatar answered Sep 23 '22 06:09

tominardi


I dont' think the validation should be done by the view as @Yiğit Güler proposes. Here is my solution:

serializers.py

from django.contrib.auth import password_validation
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers

class ChangePasswordSerializer(serializers.Serializer):
    old_password = serializers.CharField(max_length=128, write_only=True, required=True)
    new_password1 = serializers.CharField(max_length=128, write_only=True, required=True)
    new_password2 = serializers.CharField(max_length=128, write_only=True, required=True)

    def validate_old_password(self, value):
        user = self.context['request'].user
        if not user.check_password(value):
            raise serializers.ValidationError(
                _('Your old password was entered incorrectly. Please enter it again.')
            )
        return value

    def validate(self, data):
        if data['new_password1'] != data['new_password2']:
            raise serializers.ValidationError({'new_password2': _("The two password fields didn't match.")})
        password_validation.validate_password(data['new_password1'], self.context['request'].user)
        return data

    def save(self, **kwargs):
        password = self.validated_data['new_password1']
        user = self.context['request'].user
        user.set_password(password)
        user.save()
        return user

views.py

from rest_framework import status
from rest_framework.generics import UpdateAPIView
from rest_framework.authtoken.models import Token

class ChangePasswordView(UpdateAPIView):
    serializer_class = ChangePasswordSerializer

    def update(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.save()
        # if using drf authtoken, create a new token 
        if hasattr(user, 'auth_token'):
            user.auth_token.delete()
        token, created = Token.objects.get_or_create(user=user)
        # return new token
        return Response({'token': token.key}, status=status.HTTP_200_OK)

like image 39
p14z Avatar answered Sep 21 '22 06:09

p14z