1

Update #1 – 7/26/23: Per the comment by @thebjorn, I attempted to set the Content-Type: 'application/x-www-form-urlencoded and set the token name to csrfmiddlewaretoken but the error still persists.

For reference, here are the implementations I tested, incase I am missing anything:

  1. Adding the CSRF Token and Content-Type directly to the headers in the request:
headers: new Headers({
  'Content-Type' : 'application/x-www-form-urlencoded',
  'csrfmiddlewaretoken': csrftoken
})
  1. Appending the token to the form data: coverPhotoForm.append('csrfmiddlewaretoken')
  2. Including both csrfmiddlewaretoken and X-CSRFToken (seemed redundant but tested for continuity).

Original Post: I am trying to use Django's CSRF protection when sending a dynamically created FormData object, containing an image file, to a Django endpoint using JavaScript fetch POST request. My issue is that I have been unable to successfully attach the csrf_token to the request, which is causing a CSRF token missing error.

Per Django's CSRF Documentation, I have included CSRF_COOKIE_HTTPONLY = True in settings.py, and then I am setting the hidden {% csrf_token %} in my HTML file, and trying to access it in my JavaScript file:

// Create form to send image
let coverPhotoForm = new FormData();
        
// Read CSRF token and image file from DOM
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
const coverPhotoUpload = document.querySelector('#cover-photo-upload');

// Add CSRF token and image to form object
coverPhotoForm.append('X-CSRFToken', csrftoken)
coverPhotoForm.append('cover_photo', coverPhotoUpload.files[0]);

// Add image via POST request to endpoint
  fetch(`/profile/${profileUserId}`, {
    method: 'POST',
    body: coverPhotoForm,
    mode: 'same-origin'
  })
  .then( response => response.json())
  .then( response => {
    console.log(response)
  })
})

Notes:

  1. csrftoken seems to be read from the DOM successfully when checked via console.log.
  2. When the page is refreshed, images are uploaded successfully, but the CSRF token missing error still occurs each time a request is sent.

I have also tried setting the headers directly within the fetch request above, and using the Django docs example below, but that didn't change anything.

const request = new Request(
    fetch(`/profile/${profileUserId},
    {
        method: 'POST',
        headers: {'X-CSRFToken': csrftoken},
        mode: 'same-origin'
    }
);

Additional Attempts:

  1. My design above is adapted from the accepted answer from this SO post: Proper Django Fetch Validation.
  2. Tried Adding accept and content-type keys to the headers dict in my request, suggested here: POST request Using JS/Django.
  3. Added the @ensure_csrf_cookie decorator to my view as suggested in the 'Warning' in the Django Docs link included above for dynamically created forms.
  4. Used csrfmiddlewaretoken: '{{ csrf_token }}' (instead of querySelector to read from DOM) per: Using CSRF Token in JS
DAK
  • 116
  • 1
  • 10
  • If you're sending as form data, then you'll need to set the header `'Content-Type': 'application/x-www-form-urlencoded'` and send the csrf-token in the form data with name `csrfmiddlewaretoken`. I'm unsure if you need to set the `X-CSRFToken` headers when using form-data, but it doesn't hurt. – thebjorn Jul 25 '23 at 22:46
  • @thebjorn I could not fit my response in the comments, so I updated the post with the results of my testing, per your suggestion. Unfortunately, I have not gotten it to work, but if I am missing something, please let me know. – DAK Jul 26 '23 at 16:26

0 Answers0