Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django 1.9 AJAX form CSRF token 403 error - "CSRF cookie not set"

I've seen a lot about this on SO, but nothing can fix my problem.

Problem:

With CSRF middleware enabled, Django responds with 403 on AJAX form request, stating:

"CSRF cookie not set."

Following the documentation, a JS functionality was implemented, that sets custom "X-CSRFToken" header.

It works as expected, gets "csrftoken" cookie from browser and posts it along with AJAX request:

x-csrftoken: 1a0u7GCQG0wepZHQNThIXeYpMy2lZOf2

But response is still 403.

Tried solutions:

I've tried everything I could find on SO or web, specifically:

  • Checking that middleware is enabled:

    MIDDLEWARE_CLASSES = [
        ...
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        ...
    ]
    
  • Different browsers with cookies enabled;

  • Decorating my view with @ensure_csrf_cookie;

  • Setting {% csrf_token %} in my template;

  • Using render shortcut which takes right request context;

  • Setting custom CSRF_COOKIE_NAME and CSRF_HEADER_NAME in my settings.py;

  • Explicitly setting CSRF_COOKIE_SECURE = False and CSRF_COOKIE_HTTPONLY = False;

  • Explicitly setting CSRF_TRUSTED_ORIGINS setting;

  • Testing on development and production server;

  • Even request.META["CSRF_COOKIE_USED"] = True in my view, as someone suggested.

And still got nothing.

Headers:

If I use @csrf_exempt and print(request.META) in my view, it's clear that custom header "X-CSRFToken" is present in request and formatted according to Django documentation, with "HTTP_" prefix, replaced hyphens with underscores, all uppercase: "HTTP_X_CSRFTOKEN".

Even more, it's value matches with cookie set by Django.

Cookies:

Strange thing is, if I try to print(request.COOKIES) in my view, on page and form load I can see "csrftoken" cookie there, but dictionary is empty on AJAX request. Can it be the problem?

Desperate to find what is actually wrong. Thank you for reading this.

like image 273
Damaged Organic Avatar asked Mar 08 '16 13:03

Damaged Organic


1 Answers

Ok, the issue is quite simple then:

Fetch API is not sending credentials by default. According to MDN:

The credentials read-only property of the Request interface indicates whether the user agent should send cookies from the other domain in the case of cross-origin requests. This is similar to XHR’s withCredentials flag, but with three available values.

Default is omit, and it never sends cookies. You just need to add same-origin to your fetch() function arguments:

fetch(formUrl, {
    ...
    credentials: 'same-origin',
    ...
})

And you'll be good to go : )

like image 51
Smile0ff Avatar answered Oct 31 '22 08:10

Smile0ff