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 %}
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...)