OrchardCMS / Orchard

Orchard is a free, open source, community-focused Content Management System built on the ASP.NET MVC platform.
https://orchardproject.net
BSD 3-Clause "New" or "Revised" License
2.37k stars 1.12k forks source link

AJAX Post in custom module returns AntiForgeryToken error #2832

Closed orchardbot closed 9 years ago

orchardbot commented 12 years ago

2LM created: https://orchard.codeplex.com/workitem/19005

When doing an AJAX Post to a controller in a custom module, the error "A required anti-forgery token was not supplied or was invalid." is thrown. This only works when Module.txt contains "AntiForgery: disabled".

You can test this by following the blog post of Ryan Keeter about the AntiForgeryToken and AJAX, or even better, use his example module. You will see that his module only works because Module.txt contains "AntiForgery: disabled". If you change this to "enabled", the above error occurs: http://ryankeeter.com/ajax-and-anti-forgery-tokens-in-orchard-cms

orchardbot commented 12 years ago

@Piedone commented:

This is expected, a POST automatically requires a token. You can add it manually when building the AJAX request, or if you submit a form already having it.

orchardbot commented 12 years ago

2LM commented:

I'm sorry, but I think there's some misunderstanding, a POST indeed requires a token, but passing a token only works when Module.txt contains "AntiForgery: disabled".

When passing a completely valid token when Module.txt contains "AntiForgery: enabled", you receive the error "A required anti-forgery token was not supplied or was invalid."

You can test this by using the sample module of Ryan Keeter at http://ryankeeter.com/ajax-and-anti-forgery-tokens-in-orchard-cms.

His module works perfectly, simply because his Module.txt contains "AntiForgery: disabled". If you change that to "enabled", it no longer works.

It might be that I have a wrong understanding of the "AntiForgery" setting in Module.txt, but to my understanding when this is "enabled", I should be able to do an AJAX POST when passing a valid token. When this is "disabled", I think the passed token should be ignored. Even this is not the case, as the system also errors when the setting in Module.txt is "disabled" and you don't pass a valid token.

Please shed some light onto this as to my belief, something isn't really working as expected here...

orchardbot commented 12 years ago

@Piedone commented:

I think you understand that config option correctly. But I'm surprised that it doesn't work for you, the Shoutbox module (http://orchardshoutbox.codeplex.com/) has a form posted through AJAX and it works as expected, sending a token (used e.g. here: http://english.orchardproject.hu/).

orchardbot commented 12 years ago

2LM commented:

I had a look at the Shoutbox project, and the biggest difference in what I want to do and Shoutbox is that Shoutbox uses a form post, and the aforementioned module of Rian Keeter is using an AJAX post using knockoutJS.

Could it be that a $.ajax() call passing an object containing a __RequestVerificationToken property results in different behavior in regards to the Module.txt setting? It sure seems so...

orchardbot commented 12 years ago

@Piedone commented:

Take a look again :-). Although the Shoutbox module has a standard form, there is some JS that overrides its submit event and posts the form through AJAX. That means the form data, including the token is not built by hand, but a standard form is serialized for posting. That shouldn't make any difference in terms of handling the token though.

orchardbot commented 12 years ago

2LM commented:

Hi,

Thanks for persisting :) I just had a new look and I think I may have found the issue, of which I'm not really sure whether it's intended...

In module Example.Forgery of Ryan Keeter, the following is used:

 <fieldset>
    <legend>
        Create Person
    </legend>
    <div>
        @Html.LabelFor(m => m.FirstName, T("First Name"))
        @Html.TextBoxFor(m => m.LastName, new {Data_Bind = "value: FirstName"})
    </div>
    <div>
        @Html.LabelFor(m => m.LastName, T("Last Name"))
        @Html.TextBoxFor(m => m.LastName, new {Data_Bind = "value: LastName"})
    </div>
    <div style="padding-top: 10px;">
        <button type="submit" data-bind="click: AddPerson">Add Person</button>
    </div>

</fieldset>

            var person = new Person({
                FirstName: self.FirstName(),
                LastName: self.LastName(),
                __RequestVerificationToken:'@Html.AntiForgeryTokenValueOrchard()'
            });
            $.ajax(
                "/Example.Forgery/Person/Create", {
                    data: ko.toJSON(person),
                    type: "post",
                    dataType: "json",
                    contentType: "application/json; charset=utf-8",
                    success: function (result) { alert(result); }
                }
            );

The use of @Html.AntiForgeryTokenValueOrchard() works perfectly as long as Module.txt contains "AntiForgery: disabled". When changing this to "enabled", this fails miserably :(

When I change Ryan's code to use "@using (Html.BeginFormAntiForgeryPost())" wrapped around his fieldset, and change his post data to use the hidden __RequestVerificationToken field as follows, everyting works as expected:

            var person = new Person({
                FirstName: self.FirstName(),
                LastName: self.LastName(),
                __RequestVerificationToken:$(':input[name="__RequestVerificationToken"]').val(); //'@Html.AntiForgeryTokenValueOrchard()'
            });

While I can see the result of it working now, I'm a bit confused as to why "@Html.AntiForgeryTokenValueOrchard()" doesn't work. Is this a bug or am I missing something here?

orchardbot commented 12 years ago

@bleroy commented:

Also, make sure you have a machine key.

orchardbot commented 12 years ago

2LM commented:

Thanks for your input Bertrand, what is the machineKey for? I suspect it should be placed in machine.config or something, and if so, what with shared hosting?

Apart from that, do you have any thoughts on why "@Html.AntiForgeryTokenValueOrchard()" doesn't work with knockoutJS AJAX Posts and "@using (Html.BeginFormAntiForgeryPost())" does?

orchardbot commented 12 years ago

@bleroy commented:

The machine key is used for all crypto that needs to be done on your site. If you don't specify it, it can be regenerated, destroying your ability to validate previously rendered forms. See http://docs.orchardproject.net/Documentation/Setting-up-a-machine-key (and the release notes)

orchardbot commented 12 years ago

2LM commented:

Superb, I was unaware of this... Thanks!

orchardbot commented 12 years ago

@bleroy commented:

May I close this?

orchardbot commented 12 years ago

2LM commented:

Well, my overall issue is resolved, but I'm still clueless on why "@Html.AntiForgeryTokenValueOrchard()" doesn't work with knockoutJS AJAX Posts and "@using (Html.BeginFormAntiForgeryPost())" does?

orchardbot commented 12 years ago

2LM commented:

I just tried to add a machineKey to my web.config as generated at aspnetresources.com:

By adding this to the web.config, Orchard seems to be unable to authenticate or something, as I can't get to the site or admin anymore and am redirected to http://localhost:32321/Users/Account/AccessDenied?ReturnUrl=%2f

orchardbot commented 12 years ago

@bleroy commented:

You should never publish a crypto key publicly. This is negating the whole point of having one.

orchardbot commented 12 years ago

2LM commented:

Yes, I know, this is not my crypto key, I just copied it in from aspnetresources for illustration. Problem still remains though, any ideas? I've seen this behavior quite lot with Orchard lately, whenever something's wrong, I just get a redirection to the below url, and nothing to be found in the logs... Pretty frustrating to debug I must say. Is there some way to get info about these issues rather than getting redirected to the below, which is even an empty white page, I must add:

http://localhost:32321/Users/Account/AccessDenied?ReturnUrl=%2f

orchardbot commented 12 years ago

@bleroy commented:

I don't know, I've never seen it. File another bug with good repro steps?