Django CSRF Token with POST Throws 403 Due to No Origin Header

Viewed 43

Got migrating legacy project from Python 2.7 & Django 1.9 to Python 3.10 & Django 4.1.

There is also a bunch of JavaScript code which I only have a general idea of.

Stuck struggling with 403 Code with POST Request from url to another due to important CSRF protection:

Help

Reason given for failure:

Origin checking failed - null does not match any trusted origins.

In general, this can occur when there is a genuine Cross Site Request Forgery, or when Django’s CSRF mechanism has not been used correctly. For POST forms, you need to ensure:

Your browser is accepting cookies.
The view function passes a request to the template’s render method.
In the template, there is a {% csrf_token %} template tag inside each POST form that targets an internal URL.
If you are not using CsrfViewMiddleware, then you must use csrf_protect on any views that use the csrf_token template tag, as

well as those that accept the POST data. 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.

You’re seeing the help section of this page because you have DEBUG = True in your Django settings file. Change that to False, and only the initial error message will be displayed.

You can customize this page using the CSRF_FAILURE_VIEW setting.

Looked through the SO and found no close solution.

As far as it was discovered, JavaScript generates JSON Response as it should, namely:

{"oneParameter": [[18, 0.1], [19, 0.5], [20, 1.0], [21, 3.0], [22, 4.0], [23, 6.0]], "anotherParameter": [[33, -3], [34, -5], [35, -10], [36, -15], [37, -20], [38, -25], [39, -30], [40, -35], [41, -40], [42, -45], [43, -50]]}

However, this JSON has no place to store requisites of the csrfmiddlewaretoken.

Upon reference to the documentation https://docs.djangoproject.com/en/4.1/ref/csrf/#rejected-requests:

New in Django 4.0: Origin checking was added, as described above.

it seems that is where the bug is concealed.

Got the following JS-code:

function markFunction() {
  if (subject_select.value) {
    $.ajax({
        type: 'POST',
        url: '/autocomplete-one-parameter-another-parameter/',
        timeout: 3000,
        async: true,
        tryCount: 0,
        retryLimit: 3,
        dataType: 'json',
        data: {
        // Modification Required Here, I suppose >>
          id_subject: subject_select.value,
          csrfmiddlewaretoken: getCookie('csrftoken')
        },
        success: function (data) {
          if (data) {
              var anPt = document.getElementById('id_anotherParameter');
              removeOptions(anPt);
              var oneParameters = document.getElementById('id_oneParameters');
              removeOptions(oneParameters);
              var anPtsLength = data['anotherParameters'].length;
              console.log("anPtsLength", anPtsLength);
              var oneParametersLength = data['oneParameters'].length;
              for (var i = 0; i < anPtsLength; i++) {
                  temp = document.createElement("option");
                  temp.value = data['anotherParameters'][i][0];
                  temp.text = data['anotherParameters'][i][1];
                  anPt.add(temp, null);
              }
              for (var i = 0; i < oneParametersLength; i++) {
                  temp = document.createElement("option");
                  temp.value = data['oneParameters'][i][0];
                  temp.text = data['oneParameters'][i][1];
                  oneParameters.add(temp, null);
              }
              anPt.removeAttribute('disabled');
              oneParameters.removeAttribute('disabled');
            }
        },
        error: function(xhr, textStatus, errorThrown) {
        }
    });
  } else {
    var anPt = document.getElementById('id_anotherParameter');
    removeOptions(anPt);
    anPt.setAttribute('disabled', 'disabled');
    var oneParameters = document.getElementById('id_oneParameters');
    removeOptions(oneParameters);
    oneParameters.setAttribute('disabled', 'disabled');
  }
}

Upon some research, has either of two options how to integrate Origin header:

const csrftoken = getCookie('csrftoken');

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
  jqXHR.setRequestHeader('X-CSRFToken', csrftoken);
});

or that from Documentation:

const request = new Request(
    '/autocomplete-one-parameter-another-parameter/',
    {
        method: 'POST',
        headers: {'X-CSRFToken': csrftoken},
        mode: 'same-origin' // Do not send CSRF token to another domain.
    }
);
fetch(request).then(function(response) {
    // ... << Don't understand how to integrate this to the JS above
});

Please can anyone explain which of the options shall be chosen and help with integrating these two pieces of chain?

0 Answers
Related