0

I have been successfully using the AntiForgery option with Ajax in Orchard Modules for a while. Recently, I have been wanting to change from using the default ContentType = 'application/x-www-form-urlencoded; charset=UTF-8' to a JSON payload (ContentType='application/JSON').

As soon as I do this I get an exception thrown by ASP.NET 'A required anti-forgery token was not supplied or was invalid.'. OK, but how do I go about adding the __RequestVerificationToken while preserving JSON payload?

For reference, here is the code I'm using:

    var config = {
        url: url,
        type: "POST",
        data: data ,
        dataType: "json",
        contentType: "application/json; charset=utf-8"
    };
    $.ajax(config);

Controller (blows up with 'A required anti-forgery token was not supplied or was invalid.' before it gets here):

    [HttpPost]
    public ActionResult Update(ShoppingCartItemVM[] items)
    {
       // do stuff
    }

Is this a limitation of the Orchard AntiForgery wrapper or of the MVC AntiForgery functionality? Or am I being stupid (again)?

user772436
  • 51
  • 11
  • Why do you need to send JSON? Either you're talking to your Orchard server, and it will understand post just well (including binding complex array parameters like the one you have there) or it's an external service and it won't care about anti-forgery. – Bertrand Le Roy Sep 20 '12 at 01:10
  • Json is not a requirement. But I stumble repeatedly in this area and need to know if it's me or some limitation I am not aware of. As Mathew and Giscard make clear, it's me! Thanks for your help. – user772436 Sep 20 '12 at 22:37

2 Answers2

2

Giscard is correct. I'll dig a bit deeper.

Note: Only the "post" results in orchard controller require the anti forgery token. So there is less of a requirement to remember that where using a "Get" in a request for json.

Often you will want to send more data than just the request token. In that case the 'data' object you send with your request must contain that __RequestVerificationToken value. In that case jQuery is useful for example:

var defaultPostValues = { __RequestVerificationToken:'@Html.AntiForgeryTokenValueOrchard()', id: 1, ..etc.. };
var myValues = { answers: [1,5,5,10] };
var data = $.extend({}, defaultPostValues, myValues); 

var config = {
    url: url,
    type: "POST",
    data: data ,
    dataType: "json",
    contentType: "application/json; charset=utf-8"
};
$.ajax(config);

The anti-forgery token can also be turned off per module definition (if I remember correctly?). Module.txt

Name: Polls
AntiForgery: false
Author: Matt
... removed for brevity 
Features:
    Polls
... etc

However I would recommend using the antiforgery if your calls are within Orchard's modules, and disabling if and only if your data is needed else where by external requests. But I would recommend WebAPI within Orchard for that case but that creates a whole new story and probably likely moves far out of scope.

Matthew
  • 412
  • 4
  • 16
  • You can turn it off per module, and also per controller action. My example is untested, but I thought it would work even if there were already some values assigned to the `data` var (see here, for how to assign new key/val pairs to an existing javascript object: http://stackoverflow.com/a/4071599/239185). – Giscard Biamby Sep 20 '12 at 14:20
  • Both you and Giscard correctly surmised that the problem was in the Json data formatting. Thanks. – user772436 Sep 20 '12 at 22:33
0

Maybe try this:

​data = {color: 'red', weight:'20lbs'};

// do some more work...

// Append the anti-forgery token to the POST values: 
data['__RequestVerificationToken'] = '@Html.AntiForgeryTokenValueOrchard()';

// Make the .ajax() call: 
var config = {
    url: url,
    type: "POST",
    data: data ,
    dataType: "json",
    contentType: "application/json; charset=utf-8"
};
$.ajax(config);

If you are forming the json somewhere other than a razor view, you can do the @Html.AntiForgeryTokenValueOrchard() inside a razor view and pass it to a javascript object or variable so you can add it to the json via javascript.

EDIT: In addition to the method Matthew posted, you can also append the anti-forgery token to the POST values right before you make the AJAX call without using .extend(). Example: http://jsfiddle.net/JC66L/.

Giscard Biamby
  • 4,569
  • 1
  • 22
  • 24