I needed to create a file upload through Ajax in my application and after an infuriating few hours (due to the encryption on the CSRF token) I came up with this "hack".
function sendFile(file)
{
var formData = new FormData();
formData.append("image", file);
var ajax = $.ajax({
type: 'get',
url: '/route/to/my/ping/method'
})
.done(function() {
var token = readCookie('XSRF-TOKEN');
$.ajaxSetup({
headers: {
'X-XSRF-TOKEN': decodeURIComponent(token)
}
});
var ajax = $.ajax({
type: 'post',
url: '/route/to/my/upload/method',
data: formData,
contentType: false,
processData: false
})
.done(function() {
alert("success");
})
.fail(function() {
alert("fail");
});
});
}
Edit - Does anybody know of any better ways of achieving the same without wrapping the file input in a form generated by Illuminate Form Builder?
@MThomas I think you misunderstood me or I wasn't too clear, the CSRF token is very much dependant on the Form Builder as it is automatically inserted when creating a form or when using Form::token() or csrf_token(). However, if you created a form outside of Laravel with just some HTML and had just a bit of JavaScript, submitting that form it would fail because there was no CSRF token.
Thus, the hack to get the token first before submitting the file upload through Ajax.
All the Form builder does is add a hidden field <input name="_token" type="hidden" value="yourtokenhere">
You could just add this to a form you make with pure html.
When you say hack? What is the problem? Publishing the token to JS? Getting the token out of JS? Submitting the token? Unsure what you see as the problem. I've written a ton of AngularJS code and have worked around security CSRF issues in a number of ways, including writing my own.
Set a meta tag called "_token" using csrf_token().
You can then tell jQuery to pass this token as a custom header for AJAX requests.
Modify your CSRF filter - if the request is an AJAX request, tell it to retrieve the token from the request header rather than Input::get().
@jakeryansmith, @MThomas, @mibu31, thank you for the replies however as per my example, csrf_token only works inside of Laravel, if I had a form outside of Laravel that needed to post to a route I would have no way to pull in the token.
@mibu31 just to add to your answer, adding csrf_token to your header won't work in L5 as it needs to be encrypted to be matched correctly and the method csrf_token gives an unencrypted token.
@nolros the problem is posting to Laravel from outside of Laravel, perfect example is an SPA. The hack allows you to do a "ping" to Laravel to fetch the encrypted token and then add it to the headers for the post.