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

Posting ajax request with Json content and antiforgery #5319

Open apolun opened 9 years ago

apolun commented 9 years ago

Hi to all I want to share a solution for the problem of MVC controllers when working with JSON and antiforgery requests to server. First of all I get the exceptions about 'RequestVerificationToken' There are several hacks that we could do to send these requests such as removing content type from ajax config or send objects and serialize and deserialize them from an array of string but Here is my solution with a few line of code in 'AntiForgeryAuthorizationFilter ' class and hope someone commit this change to master branch Just search for 'AntiForgeryAuthorizationFilter' class and change the OnAuthorization to this

public void OnAuthorization(AuthorizationContext filterContext)
        {
            if ((filterContext.HttpContext.Request.HttpMethod != "POST" ||
                 _authenticationService.GetAuthenticatedUser() == null) && !ShouldValidateGet(filterContext))
            {
                return;
            }

            if (!IsAntiForgeryProtectionEnabled(filterContext))
            {
                return;
            }

            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                var antiForgeryCookie = filterContext.HttpContext.Request.Cookies[AntiForgeryConfig.CookieName];

                var cookieValue = antiForgeryCookie != null
                    ? antiForgeryCookie.Value
                    : null;

                System.Web.Helpers.AntiForgery.Validate(cookieValue, filterContext.HttpContext.Request.Headers["__RequestVerificationToken"]);
            }
            else
            {
                var validator = new ValidateAntiForgeryTokenAttribute();
                validator.OnAuthorization(filterContext);

                if (filterContext.HttpContext is HackHttpContext)
                    filterContext.HttpContext = ((HackHttpContext)filterContext.HttpContext).OriginalHttpContextBase;
            }
        }

and in ajax request :

       var token = $(':input[name="__RequestVerificationToken"]').val();
         var config = {
            url: url,
            type: "POST",
            headers: { "__RequestVerificationToken": token },
            data: 'put something here from simple to complex array of objects it will be bind successfully' ,
            contentType: "application/json; charset=utf-8"
        };
sebastienros commented 9 years ago

I want to share a solution for the problem of MVC controllers when working with JSON and antiforgery requests to server.

What is the problem exactly you are trying to solve ?

sfmskywalker commented 9 years ago

I believe there is a code comment mentioning the word "hack". This solution sends the antiforgery token via an http header so the "hack" can go. I think it's clever (in a positive sense).

apolun commented 9 years ago

@sebastienros The problem is in binding of Json and our Models when working with anitforgery tokens Specially when you try to send a collection of objects When you want to update your model with ajax There are solutions available something like this http://ryankeeter.com/ajax-and-anti-forgery-tokens-in-orchard-cms is work fine when you define antiforgerytoken in your model which I mark as bad practice specially when you try to update a collection of objects forexample list of persons in the example But I only append "one if" for ajax request to validate our Request and after that Everything works fine No change in our model no worry about sending arrays of objects and bindings and life is beautiful :)

maxwebster commented 7 years ago

@apolun I had to also use your version for MVC 4 but I didn't change the core just overrode using suppressDependency but for each new version of orchard there is a new version of AntiForgeryAuthorizationFilter so I too would like this in the master branch

sebastienros commented 7 years ago

The recommended solution has been posted on StackOverflow http://stackoverflow.com/questions/9029402/orchard-cms-ajax-anti-forgery-token-when-logged-in/9039504#9039504

maxwebster commented 7 years ago

Thanks but some more hours retesting this approach. This approach as stated above, does not work with a json model

sebastienros commented 7 years ago

I understand the issue.

The solution you proposed has a flaw though. If someone is already using the form body to pass the AFT during an ajax call, then your code would not test it. Hence we need to check the presence of the value in the header, otherwise fallback to the standard validation.

Also might be interesting to see what options are available in ASP.NET Core about the cookie names. I know Angular is using a specific header name by default. we might want to support changing the header name too by configuration.

And then we might probably update the documentation and the SO answer as this is a better technique.

Niko88 commented 6 years ago

Hello everyone, after hours of trying to override Orchard's default AntiForgery filter without changing the source I found this blog post that saved me from further stress, suggesting the use of the action filter [ValidateAntiForgeryTokenOrchard(false)] https://weblogs.asp.net/bleroy/opting-out-of-anti-forgery-validation-in-orchard