Question

I am making the backend for a mobile app and using Django with Userena for the user management. I made the sign in and sign up using Django REST framework and everything works fine. The only thing I need to do now is to implement the "forget password" functionality. I wanted to use the already implemented one from Userena, but I cannot get rid of the error "CSRF token missing or incorrect" even after using the csrf_exempt dectorator. What am I doing worng?

urls.py

from django.contrib.auth.views import password_reset
from django.views.decorators.csrf import csrf_exempt
...
urlpatterns = patterns(
    '',
    url(r'^password/mobile/reset/$',
       csrf_exempt(password_reset),
       {'template_name': 'userena/password_reset_form.html',
        'email_template_name': 'userena/emails/password_reset_message.txt',
        'extra_context': {'without_usernames': userena_settings.USERENA_WITHOUT_USERNAMES}
        },
       name='userena_password_mobile_reset'),
)

passowrd_reset_form.html

{% extends 'userena/base_userena.html' %}
{% load i18n %}

{% block title %}{% trans "Reset password" %}{% endblock %}

{% block content %}
<form action="" method="post">
  <fieldset>
    <legend>{% trans "Reset Password" %}</legend>
    {% csrf_token %}
    {{ form.as_p }}
  </fieldset>
  <input type="submit" value="{% trans "Send password" %}" />
</form>
{% endblock %}
Was it helpful?

Solution

If you do a GET request before POSTing to the password reset view, you get the CSRF token in a cookie, which you can then send in your POST request.

If you insist on exempting the view: I think the problem lies in the way the CSRF protection is applied to the password_reset view. It is explicitly decorated by csrf_protect.

To have a closer look at the problem, lets assume original_password_reset_view is password_reset without the csrf_protect. Basically, you are doing this:

csrf_exempt(csrf_protect(original_password_reset_view))
# ^^ your code
#           ^^ the decorator in django.contrib.auth.views

And adding in the effect of the CsrfViewMiddleware, we get the equivalent of

csrf_protect(csrf_exempt(csrf_protect(original_password_reset_view)))

csrf_protect is just a middleware-turned-decorator from CsrfViewMiddleware. csrf_exempt on the other hand simply sets csrf_exempt=True on its argument. So the middleware, represented by the outer csrf_protect, sees the csrf_exempt=True value on the view and disables its CSRF projection. It negates the outer csrf_protect. So we have:

csrf_protect(original_password_reset_view)

The view is still protected. Basically, there is no sane way around. (An insane way: write a middleware that sets request.csrf_processing_done = True for that specific URL. Don't do that...)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top