7

I have a login form that logs the users into the admin site. It works fine in development, and mostly works fine in production, but sometimes it gives a 403 CSRF verification failed error. Note that this happens to users that were able to log in before, so I can't imagine it's an issue with their browser.

It looks like jenniwren had a similar issue in this comment. They never asked a question about it, and the other commenters had no clue why that would happen.

Here's what I have:

urls.py

urlpatterns += patterns('django.contrib.auth.views',
    url(r'^logout$', 'logout', {'next_page': 'mysite_login'}, name='mysite_logout'),
    url(r'^login$', 'login', name='mysite_login'),

    url('^', include('django.contrib.auth.urls')),
)

main/registration/login.html

{% extends "base.html" %}
{% load staticfiles %}

{% block content %}
    {% if form.errors and not form.non_field_errors %}
        <p class="errornote">Please correct the error(s) below.</p>
    {% endif %}

    {% if form.non_field_errors %}
        {% for error in form.non_field_errors %}
            <p class="errornote">
                {{ error }}
            </p>
        {% endfor %}
    {% endif %}

    <form action="{{ app_path }}" method="post" id="login-form">
        {% csrf_token %}

        <div class="form-row">
            {% if form.errors %}
                { form.username.errors }}
            {% endif %}
            {{ form.username.label_tag }}
            {{ form.username }}
        </div>
        <div class="form-row">
            {% if form.errors %}
                {{ form.password.errors }}
            {% endif %}
            {{ form.password.label_tag }}
            {{ form.password }}
        </div>
        <input type="hidden" name="next" value="{{ next }}" />

        <div class="submit-row">
            <input type="submit" value="Log in" />
        </div>

        <div class="password-reset-link">
            <a href="{% url 'password_reset' %}">Forgot your password?</a>
        </div>
    </form>
{% endblock content %}

settings.py

INSTALLED_APPS = (
    'filebrowser',
    'grappelli',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'psycopg2',
    'main',
    'mysite'
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware'
)

SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True
CSRF_COOKIE_HTTPONLY = True
Community
  • 1
  • 1
NJP
  • 815
  • 1
  • 7
  • 20

4 Answers4

3

This problem might occur if:

  1. A user opens the login page in two different tabs
  2. A user logins in one tab.
  3. A user tries to login again in a different tab (although he is already logged in).

If your CSRF_FAILURE_VIEW shows your site template, you might be able to let the users know they are already logged in, and do not need to refresh the page.

Udi
  • 29,222
  • 9
  • 96
  • 129
1

This is a message I got whed Debug=True:

The form has a valid CSRF token. After logging in in another browser tab or hitting the back button after a login, you may need to reload the page with the form, because the token is rotated after a login.

orlyohreally
  • 410
  • 6
  • 19
0

I think this answer will help you, you can also use @csrf_exempt annotation over your function(view) like this:

@csrf_exempt
def foo():
   return 'bar'

django doc on this subject: https://docs.djangoproject.com/en/3.0/ref/csrf/

A Khalili
  • 192
  • 2
  • 10
0

I was doing some proxying for the admin url and fixed my issue by editing my settings.py file and adding:

CSRF_TRUSTED_ORIGINS = [".subdomain.com"]

Using my organisation's gsuite subdomain. See the django docs for reference