How to include the CSRF token in the headers in Dropzone upload request?
Asked Answered
F

13

41

I am working on a single page application and I am using Laravel 5 for the web service.

All forms are submitted asynchronously and I use a beforeSend on them to attach the CSRF token which I take from the meta tag like so:

$.ajax({
    url: '/whatever/route',
    type: 'POST',
    dataType: 'JSON',
    data: $('form#whatever-form').serialize(),
    beforeSend: function(request) {
        return request.setRequestHeader('X-CSRF-Token', $("meta[name='token']").attr('content'));
    },
    success: function(response){
        rivets.bind($('#whateverTag'), {whateverData: response});
    },
    error: function(response){
    }
});

All my forms work fine but dropzone upload doesn't. It gives me back a TokenMismatchException exception. Here is my dropzone code to update the profile photo:

$("#mydropzone").dropzone({
    url: "/profile/update-photo",
    addRemoveLinks : true,
    maxFilesize: 5,
    dictDefaultMessage: '<span class="text-center"><span class="font-lg visible-xs-block visible-sm-block visible-lg-block"><span class="font-lg"><i class="fa fa-caret-right text-danger"></i> Drop files <span class="font-xs">to upload</span></span><span>&nbsp&nbsp<h4 class="display-inline"> (Or Click)</h4></span>',
    dictResponseError: 'Error uploading file!'
});

I have tried putting the beforeSend in here too:

$("#mydropzone").dropzone({
    url: "/profile/update-photo",
    addRemoveLinks : true,
    maxFilesize: 5,
    dictDefaultMessage: '<span class="text-center"><span class="font-lg visible-xs-block visible-sm-block visible-lg-block"><span class="font-lg"><i class="fa fa-caret-right text-danger"></i> Drop files <span class="font-xs">to upload</span></span><span>&nbsp&nbsp<h4 class="display-inline"> (Or Click)</h4></span>',
    dictResponseError: 'Error uploading file!',
    beforeSend: function(request) {
        return request.setRequestHeader('X-CSRF-Token', $("meta[name='token']").attr('content'));
    },
});

I have also tried to put a global ajaxSetup in my main file like so:

$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="token"]').attr('content')
    }
});

It is still not working. What am I doing wrong? How can I pass the CSRF token in the header with the dropzone upload so as to not a get an exception?

Fredrika answered 10/5, 2015 at 7:54 Comment(2)
You wrote "Dropbox," but I don't see anything in your question that relates to Dropbox. Perhaps you meant to say Dropzone? I'm going to remove the Dropbox tag for now.Arose
Thank you for bringing it to my notice. That was so dumb of me.Fredrika
F
69

Okay so this code is working just fine now:

$("#mydropzone").dropzone({
    url: "/profile/update-photo",
    addRemoveLinks : true,
    maxFilesize: 5,
    dictDefaultMessage: '<span class="text-center"><span class="font-lg visible-xs-block visible-sm-block visible-lg-block"><span class="font-lg"><i class="fa fa-caret-right text-danger"></i> Drop files <span class="font-xs">to upload</span></span><span>&nbsp&nbsp<h4 class="display-inline"> (Or Click)</h4></span>',
    dictResponseError: 'Error uploading file!',
    headers: {
        'X-CSRF-TOKEN': $('meta[name="token"]').attr('content')
    }
});

So basically I needed to add the X-CSRFToken in the header of the Dropzone request. Works like charm now.

Fredrika answered 10/5, 2015 at 8:40 Comment(4)
Documentation on Dropzone site: dropzonejs.com/#config-headersSpout
just wanted to clear up the naming a little bit, the default Laravel install is looking for 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content'). laravel.com/docs/5.5/csrf#csrf-x-csrf-tokenCoriolanus
Could you edit the code and change the header parameter from X-CSRFToken to X-CSRF-Token. Someone new could spend many hours figuring out that small bit.Ease
For Yii (PHP) framework, it works when I set as 'X-CSRF-TOKEN' or 'X-Csrf-Token' in Dropzone config and read as $_SERVER['HTTP_X_CSRF_TOKEN'] on server side. But if I set as 'X_CSRF_TOKEN' in Dropzone config, it does not show on server side. Just a note, not complaining.Salas
C
7

You can add csrf token for every jquery ajax request within your application with these code.

$.ajaxSetup({
    headers: {
        'X-CSRF-Token': $('meta[name="_token"]').attr('content')
    }
});
Caffrey answered 11/5, 2015 at 11:21 Comment(1)
Per the jQuery doc on this function, "its use is not recommended." Also, I think this might only affect AJAX calls made via $.ajax() in the jQuery library?Spout
G
3

This also works pretty well :

$("#mydropzone").dropzone({
  url: "/profile/update-photo",
  addRemoveLinks : true,
  maxFilesize: 5,
  dictResponseError: 'Error uploading file!',
  headers: {
    'X-CSRF-Token': $('input[name="authenticity_token"]').val()
  }
});
Galicia answered 5/6, 2017 at 2:48 Comment(0)
G
3
Dropzone.autoDiscover = false;
        // or disable for specific dropzone:
        // Dropzone.options.myDropzone = false;

        $(function () {
            // Now that the DOM is fully loaded, create the dropzone, and setup the
            // event listeners

            var myDropzone = new Dropzone("#my-awesome-dropzone");
            myDropzone.on("addedfile", function (file) {
                /* Maybe display some more file information on your page */
            });
            myDropzone.on("sending", function (file, xhr, formData) {
                 formData.append('csrfmiddlewaretoken', document.getElementsByName('csrfmiddlewaretoken')[0].value);
                /* Maybe display some more file information on your page */
            });
        });

You could include it this way.

Gerfen answered 12/6, 2019 at 15:26 Comment(0)
H
1

I believe the best way to handle this is to set it by default for all ajax posts (with jQuery) as according to the Django docs

https://docs.djangoproject.com/en/1.8/ref/csrf/#ajax

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));
}

function sameOrigin(url) {
    // test that a given url is a same-origin URL
    // url could be relative or scheme relative or absolute
    var host = document.location.host; // host + port
    var protocol = document.location.protocol;
    var sr_origin = '//' + host;
    var origin = protocol + sr_origin;
    // Allow absolute or scheme relative URLs to same origin
    return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
        (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
        // or any other URL that isn't scheme relative or absolute i.e relative.
        !(/^(\/\/|http:|https:).*/.test(url));
}

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
            // Send the token to same-origin, relative URLs only.
            // Send the token only if the method warrants CSRF protection
            // Using the CSRFToken value acquired earlier
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

In your example, you have a typo when adding it to the Dropzone.js ajax post.

'X-CSRF-Token'

should be

'X-CSRFToken'

Hegumen answered 19/8, 2015 at 19:28 Comment(2)
Setting it to default ajax posts creates another issue that I encountered later on. I was trying to consume a third party service and got stuck because I could not understand what the error was. Later I realized, the default settings have been sending the CSRF token to the third party and it had been causing a problem. :)Fredrika
Good point and worth remembering! Would say judgement call. If you have far more local ajax calls in your code then perhaps making an exception for remote calls would make more sense, or inverse when the opposite is true. Whatever ultimately yields less work. cheers!Hegumen
S
1

For anyone using default Laravel setup:

window.Laravel = {!! json_encode([
    'csrfToken' => csrf_token(),
]) !!};

Dropzone.options.attachments = {
    url: 'upload',
    headers: {
        'X-CSRF-TOKEN': Laravel.csrfToken
    }
}
Swanson answered 12/4, 2017 at 20:58 Comment(0)
I
1

For those of you that came here and are looking for the Rails solution, add the header with the following code:

  headers: {
    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
  },

The same applies for Laravel 6.x according to the docs: https://laravel.com/docs/6.x/csrf#csrf-x-csrf-token

$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});
Impolitic answered 27/9, 2019 at 13:13 Comment(0)
D
1

Wow! Amazing feedback and suggestions! I took a bit of every reply and made it fit to what I needed.

Thus, let me pass-it-forward with the code that I am now using for my FLASK server using Flask-WTF and the "X-CSRF-Token" Dropzone Header.

<form>
<div class="form-horizontal">
    <div class="upload-drop-zone" id="drop-zone-licenseKey">
        <div class="dz-message">
            Drag and Drop, or Click to<br> enter your new license key
        </div>
    </div>
    <script>
        var uploadLicenseKey = new Dropzone("div#drop-zone-licenseKey",{
        init: function() 
            {
                // Do Stuff
            },
        url: "/myLicenseURL",
        paramName: "myKey",
        maxFilesize: 1, //MB,
        maxFiles: 1,
        uploadMultiple: false,
        addRemoveLinks: true,
        autoProcessQueue: false, // do not upload until save is pressed
        acceptedFiles: ".txt",
        headers: { "X-CSRF-Token" : "{{ csrf_token() }}" }
        });
    </script>
</div>
Dorina answered 18/1, 2022 at 20:49 Comment(0)
M
0

We can set CSRF token in request header.

 xhr = open("POST",logURL,true);
      //Set CSRF token in request header for prevent CSRF attack.
 xhr.setRequestHeader(CSRFHeaderName, CSRFToken);
Musician answered 4/12, 2015 at 6:26 Comment(0)
D
0
you can add a headers.

var myDropzone = new Dropzone("#drop_id", {
    url: "/upload/",
    headers: {'x-csrftoken': $.cookie('csrftoken')},
    method:"post",  
    ...
}
Danialdaniala answered 5/5, 2018 at 15:1 Comment(1)
yes, that is the same solution the OP has himself posted and acceptedLethalethal
C
0

Django solution (thanks to @Rohan):

headers: {
    'X-CSRFTOKEN': $('meta[name="token"]').context.cookie.split('=')[1]
},
Cheriecherilyn answered 22/8, 2021 at 22:58 Comment(0)
W
0

None of the other answers seem to point out that you first need the meta tag added to your layout blade file, presumably because the default blade layout files have it, but for ease of reference, it can be added as follows:

<meta name="csrf-token" content="{{ csrf_token() }}">

You can then reference the X-CSRF-TOKEN header in your Dropzone call's parameters:

Dropzone.autoDiscover = false;
jQuery(document).ready(function($) {
  $("div#uploader").dropzone({ 
        headers: { 
            'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        }, 
        paramName: 'attachment', 
        url: "/upload/path"
  });
});
Watercourse answered 4/10, 2021 at 15:45 Comment(0)
M
0

Using Laravel 9, Attach this method at configuration field ...has worked fine to me!

 sending: function(file, xhr, formData) {
        formData.append("_token", "{{ csrf_token() }}");
    },

From Dropzone CSRF token mismatch Laravel 5

Menorrhagia answered 5/7, 2023 at 16:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.