Search code examples
djangodjango-rest-frameworkdjango-formsdjango-viewsdjango-authentication

Custom Django Password Reset - Link does not get invalidated


I know that the one time link for the password reset should be invalidated following the email password change procedure as given here enter link description here. Yet, with my below implementation, although the link can reset the password each time, it does not get invalidated. The link works each time. What could be the reason for this ?

(I also see the last login timestamp is also updated in admin pages for the particular user that I am changing the password for)

(forms.py)

from django.contrib.auth.forms import UserCreationForm, SetPasswordForm 
from django.contrib.auth import get_user_model

class ResetPasswordForm(SetPasswordForm):
    class Meta:
        model = get_user_model()
        fields = ('password1', 'password2')

(tokens.py)

from django.contrib.auth.tokens import PasswordResetTokenGenerator import six

class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
    def _make_hash_value(self, user, timestamp):
        return (
            six.text_type(user.pk) + six.text_type(timestamp) +
            six.text_type(user.email_confirmed)
        )

        account_activation_token = AccountActivationTokenGenerator()

(views.py)

def activate_forgot_password(request, uidb64, token):
    try:
        uid = force_text(urlsafe_base64_decode(uidb64))
        User = get_user_model()
        user = User.objects.get(pk=uid)
    except (TypeError, ValueError, OverflowError, User.DoesNotExist):
        user = None

    if user is not None and account_activation_token.check_token(user, token):
        if request.method == 'POST':
            form = ResetPasswordForm(user, request.POST)
            if form.is_valid():
                user = form.save(commit=False)
                print(user.password)
                user.save()
                login(request, user, backend='mysite.signup.views.EmailBackend')
                return redirect('home')
        else:
            form = ResetPasswordForm(user)
            return render(request, 
                          'change_password.html', 
                          {'form': form, 
                            'uidb64': uidb64, 
                            'token': token})

    return render(request, 'account_forgot_password_token_invalid.html')

(template.html)

<form id="ResetPasswordForm" method="post" action="{% url 'activate_forgot_password' uidb64=uidb64 token=token %}" validate>
    {% csrf_token %}  
    .
    .
    .
    <div class="form-group">
        <button type="submit" id="btn-signup" class="btn btn-block btn-primary btn-lg">Change Password</button>
    </div>
</form>

Solution

  • The fields in _make_hash_value method never got update, the link still valid until one of the field assigned updated or the token timeout, you can add more fields to make sure that will trigger change like user.last_login or even user.password