How to pass along CSRF token in an AJAX post request for a form?

Viewed 71582

I'm using Scala Play! 2.6 Framework, but that may not be the issue. I'm using their Javascript routing - and it seems to work ok, but it's having issues. I have a form, which when rendered produces this, with a CSRF token:

<form method="post" id="myForm" action="someURL">

<input name="csrfToken" value="5965f0d244b7d32b334eff840...etc" type="hidden">
  <input type="text" id="sometext">
  <button type="submit"> Submit! </button>

</form>

And here's roughly, my AJAX:

$(document).on('submit', '#myForm', function (event) {

 event.preventDefault();
   var data = {
    textvalue: $('#sometext').val()
   }
 var route = jsRoutes.controllers.DashboardController.postNewProject()
 $.ajax({
    url: route.url,
    type: route.type,
    data : JSON.stringify(data),
    contentType : 'application/json',
    success: function (data) { ...      },
    error: function (data) { ...  }
        })

});

But when I post this, I am getting an UNAUTHORIZED response back from my Server, and my console in IntelliJ is telling me the CSRF check is failing. How would I pass along the CSRF token in the request?

7 Answers

From JSP

<form method="post" id="myForm" action="someURL">
    <input name="csrfToken" value="5965f0d244b7d32b334eff840...etc" type="hidden">    
</form>

This is the simplest way that worked for me after struggling for 3hrs, just get the token from input hidden field like this and while doing the AJAX request to just need to pass this token in header as follows:-

From JQuery

var token =  $('input[name="csrfToken"]').attr('value'); 

From plain Javascript

var token = document.getElementsByName("csrfToken").value;

Final AJAX Request

$.ajax({
          url: route.url,
          data : JSON.stringify(data),
          method : 'POST',
          headers: {
                        'X-CSRFToken': token 
                   },
          success: function (data) { ...      },
          error: function (data) { ...  }
});

Now you don't need to disable crsf security in web config, and also this will not give you 405( Method Not Allowed) error on console.

Hope this will help people..!!

add it to the request using the Csrf-Token header.

Thanks NateH06 for the header name! I was trying to send csrf token for a "delete" button with an ajax function call and I was stuck on the following:

@import helper._
....
<button id="deleteBookBtn" class="btn btn-danger"
        data-csrf-name="@helper.CSRF.getToken.name"
        data-csrf-value="@helper.CSRF.getToken.value"
        data-delete-url="@routes.BooksController.destroy(book.id)"
        data-redirect-url="@routes.HomeController.index()">Delete</button>

I wasn't able to add online js within the onclick() event because of a CSP set on play 2.6 too.

Refused to execute inline event handler because it violates the following Content Security Policy directive: "default-src 'self'".

And on the JS file:

function sendDeleteRequest(event) {
  url = event.target.getAttribute("data-delete-url")
  redirect = event.target.getAttribute("data-redirect-url")
  csrfTokenName = event.target.getAttribute("data-csrf-name")
  csrfTokenValue = event.target.getAttribute("data-csrf-value")
  $.ajax({
    url: url,
    method: "DELETE",
    beforeSend: function(request) {
      //'Csrf-Token' is the expected header name, not $csrfTokenName
      request.setRequestHeader(/*$csrfTokenName*/'Csrf-Token', csrfTokenValue);
    },
    success: function() {
      window.location = redirect;
    },
    error: function() {
      window.location.reload();
    }
  })
}

var deleteBookBtn = document.getElementById("deleteBookBtn");
if(deleteBookBtn) {
    deleteBookBtn.addEventListener("click", sendDeleteRequest);
}

After setting the header name as 'Csrf-Token' the ajax call works perfectly!

In case it's of use to anyone else who is googling around trying to understand why they can't get the correct token to appear.... I've been struggling with the same issue for a Play backend/React frontend combination - and hence unable to (easily) use the token-in-htmlpage technique I eventually came across another solution for setting the current token in a cookie... simply add:

play.filters.csrf {
  cookie.name = "csrftoken"
}

to application.conf and the csrftoken cookie will be set to the token. I then used https://www.npmjs.com/package/js-cookie to grab the value in my JS code and send it back in the request header - not including the code here as it's React not jQuery as for the OP and don't want to confuse matters.

As stated in the Play Framework 2.6 Documentation, you may set a 'Csrf-Token' Header with the token generated by Play:

If you are making requests with AJAX, you can place the CSRF token in the HTML page, and then add it to the request using the Csrf-Token header.

Within a Scala-Template you can get the token-value using @helper.CSRF.getToken.value

Following jQuerys Documentation you may either set it once for all Ajax requests by configuring jQuery using ajaxSetup

$.ajaxSetup({
  beforeSend: function(xhr) {
    xhr.setRequestHeader('Csrf-Token', '@helper.CSRF.getToken.value');
  }
});

or alternatively set the Header on every Request by configuring the headers object like this:

$.ajax({
  url: route.url,
  ...
  headers: {
    'Csrf-Token': '@helper.CSRF.getToken.value'
  }
});

slim csrf gurd, the way for passing csrf key and token in jQuery for slim application with twig

$(document).ready(function(){
    $.ajaxPrefilter(function(options, originalOptions, jqXHR){

        var allowedMethod = ["post", "put", "delete"];

        if (allowedMethod.includes(options.type.toLowerCase())  ) {
            // initialize `data` to empty string if it does not exist
            options.data = options.data || "";

            // add leading ampersand if `data` is non-empty
            options.data += options.data?"&":"";

            // add _token entry
            options.data += "{{csrf.keys.name}}={{csrf.name}}&{{csrf.keys.value}}={{csrf.value}}";
        }
    });
});
Related