0

I am trying to create simple WEB application using Django and React. And last weekend I've spend in attempts to fix curious bug. As you may guess my Django application views use CSRF protection.

To provide the most proper CSRF setting I used that article from django project and just copy-paste that code:

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

var csrftoken = getCookie('csrftoken');

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

into my index.js.

And $.post(...) requests (outside React classes) like that:

function registration_post() {
    var msg = $('#registration-form').serialize();
    $.ajax({
        type: 'POST',
        url: '/users/',
        data: msg,
        success: function(data) {
            if (typeof data === 'object') {
                console.log('Account successfully created.');
                console.log(data);
            } else {
                console.log('Some unnormal errors have been occurred. Here is your data:');
                console.log(data);
            }
        },
        error:  function(xhr, str){
            console.log(xhr);
        }
    });
}

works fine even without {% csrf_token %} in the form. But when I tried to run $.post(...) inside React classes I had in browser console that:

POST http://localhost:8000/expenses/ 403 (Forbidden) 

Object {readyState: 4, responseText: "{"detail":"CSRF Failed: CSRF token missing or incorrect."}", responseJSON: Object, status: 403, statusText: "Forbidden"}

To fix it I tried that:

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", $('input[name=csrfmiddlewaretoken]').val());
        }
    }
});

And even that (each block of code in uncomment state of course):

var ExpenseBox = React.createClass({
    postExpense(expense) {
        $.ajax({

            // beforeSend: function(xhr) {
            //  xhr.setRequestHeader('X-CSRFToken', csrftoken)
            // },

            // headers: {
            //  'X-CSRFToken': csrftoken
            // },

            url: this.props.get_expenses_url,
            type: 'POST',
            dataType: 'json',
            data: expense,
            success: function(data) {
                this.this.setState({data: data});
            }.bind(this),
            error: function(xhr, status, err) {
                console.log(xhr);
            }.bind(this)
        });
    },
    // other methods 
});

But it did not help. I googled it. A lot. But most of all problems solves by refering post author to the link below or fixing some curious misspelling in code but not solving my case.

I tried to make post request in Django-REST-Framework API Test mode -- it works. So, problem not in the server side permission classes.

So. Any ideas what's wrong with it?

UPD 1

Request have csrf token. Here is an example:

POST /expenses/ HTTP/1.1
Host: localhost:8000
Connection: keep-alive
Content-Length: 34
Origin: http://localhost:8000
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
X-CSRFToken: jI0qKYZybfmV4PsG0jKULqNtqMZaD49e0cJsbQRXPoNuZggqq8rpA8z0mhQJBveG
Referer: http://localhost:8000/index/
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en;q=0.8,en-US;q=0.6,ru;q=0.4
Cookie: tabstyle=html-tab; csrftoken=ELbOeq2HNCcXjvYasGihJrKnqw8ufZerQH9kHcTWylFOjstCJfgfMMVs1uX9XrNn; sessionid=7blm2q6bdmf9la2eiu7xzywznvzlnv8y
Dorman
  • 1
  • 2
  • Have you inspected the request, is the csrf header being set? – serg Oct 16 '16 at 17:44
  • @serg, of course yes. – Dorman Oct 16 '16 at 18:47
  • @serg, header example of the request was added in problem description. – Dorman Oct 16 '16 at 19:05
  • In your request example the header value doesn't match the cookie. – serg Oct 16 '16 at 22:17
  • @serg, that's right... I understand that this is not normal behavior, but why is it happens and what am I have to do with this information? – Dorman Oct 16 '16 at 22:50
  • Try to read the cookie value right before the ajax request and send it as a header value. Maybe that ajaxSetup isn't firing at every request, I am not familiar with react. Basically you need to inject the current cookie value into header on every request by any means available. – serg Oct 16 '16 at 23:29
  • @serg, thanks a lot. I found out what's wrong. After first 'GET' to /index/ django server sends cookies with csrf_token. Scripts extracts from cookies that csrf_token and do setRequestHeader in ajax "beforeSend". But after authentication django server sends NEW cookies with NEW csrf_token. So.. to fix that I extracted new csrf_token and reset it to request header. – Dorman Oct 17 '16 at 10:57
  • Possible duplicate of [How to retrieve/provide a CSRF token to/from Django as an API](http://stackoverflow.com/questions/31792770/how-to-retrieve-provide-a-csrf-token-to-from-django-as-an-api) – Paul Sweatte May 04 '17 at 19:20

0 Answers0