0
votes

I'm using django.contrib.auth.views for password reset.

I get the CSRF error when I try to submit the password change form.

It lets me enter my email, sends me a link with uidb64 and token, and then lets me enter a new password twice. When I submit this password_reset_confirm form I get the CSRF invalid error.

Here is my template for password reset confirm:

<div class="reset-page">
        <h3 class="reset-header">{% blocktrans %}Reset Password - Step 2 of 2{% endblocktrans %}</h3>
            <form class="login-form" action="" method="post">
                <div class='form'>
                    {% csrf_token %}
                    {% if validlink %}
                        <input id="id_new_password1" name="new_password1" type="password" class="text-login" placeholder="Password" />
                        <input id="id_new_password2" name="new_password2" type="password" class="text-login" placeholder="Confirm Password" />
                        <input type="submit" class="submit-login" value="{% trans 'Submit' %}" />
                    {% if error_messages %}
                        <p class="reset-error">Error: {{ error_messages }}</p>
                    {% endif %}
                    {% else %}
                        <p class="reset-bad-link">{% blocktrans %}Error: This reset link is no longer valid!{% endblocktrans %}</p>
                    {% endif %}                    
                </div>
            </form>

        <p class="reset-info">{% blocktrans %}Enter your new password, twice.{% endblocktrans %}</p>
    </div>  

I have no idea how to debug this, help would be appreciated greatly.

There isn't any custom code, just the contrib views.

One last question, in the source code of django.contrib.auth.views.password_reset_confirm it says that it doesn't need CSRF since noone can guess the URL. I've tried removing the {% csrf_token %} tag and it still didn't work. Do I need it or not?

EDIT:

The django.contrib.auth.views confirm view:

# Doesn't need csrf_protect since no-one can guess the URL
@sensitive_post_parameters()
@never_cache
def password_reset_confirm(request, uidb64=None, token=None,
                           template_name='registration/password_reset_confirm.html',
                           token_generator=default_token_generator,
                           set_password_form=SetPasswordForm,
                           post_reset_redirect=None,
                           current_app=None, extra_context=None):
    """
    View that checks the hash in a password reset link and presents a
    form for entering a new password.
    """
    UserModel = get_user_model()
    assert uidb64 is not None and token is not None  # checked by URLconf
    if post_reset_redirect is None:
        post_reset_redirect = reverse('password_reset_complete')
    else:
        post_reset_redirect = resolve_url(post_reset_redirect)
    try:
        # urlsafe_base64_decode() decodes to bytestring on Python 3
        uid = force_text(urlsafe_base64_decode(uidb64))
        user = UserModel._default_manager.get(pk=uid)
    except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
        user = None

    if user is not None and token_generator.check_token(user, token):
        validlink = True
        title = _('Enter new password')
        if request.method == 'POST':
            form = set_password_form(user, request.POST)
            if form.is_valid():
                form.save()
                return HttpResponseRedirect(post_reset_redirect)
        else:
            form = set_password_form(user)
    else:
        validlink = False
        form = None
        title = _('Password reset unsuccessful')
    context = {
        'form': form,
        'title': title,
        'validlink': validlink,
    }
    if extra_context is not None:
        context.update(extra_context)

    if current_app is not None:
        request.current_app = current_app

    return TemplateResponse(request, template_name, context)
1

1 Answers

1
votes

Remove the <div class='form'> tag. Place {% csrf_token %} right after <form class="login-form" action="" method="post">.