LP13
LP13

Reputation: 34079

ValidateAntiForgeryToken is not working asp.net mvc with ajax post

I have ASP.NET MVC 5 Application using .Net 4.5.1. The application has several jQuery ajax post method that POST data to server. To prevent cross-site request forgery (XSRF) i have added the following in _layout.cshtml

 @Html.AntiForgeryToken()

and also javascript which sends the token with all ajax POST in HttpHeader

$(document).ajaxSend(function (event, jqXHR, ajaxOptions) {
            if (ajaxOptions.type === 'POST') {
                jqXHR.setRequestHeader('__RequestVerificationToken', $('input[name="__RequestVerificationToken"]').val());
            }                
        });

and then in controller action method i have

    [ValidateAntiForgeryToken]        
    [HttpPost]
    public ActionResult Save(MyModel model)
    {
       //save and return json data
    }

However ValidateAntiForgeryToken thorwing exception

The required anti-forgery form field "__RequestVerificationToken" is not present.

i verified that __RequestVerificationToken is getting added to httpheader for every Ajax POST.

I have another application that was developed using ASP.NET Core and i have the same functionality there (Except the header name is RequestVerificationToken instead of __RequestVerificationToken) and its been working in ASP.NET Core.

Why the same is not working in ASP.NET MVC when token is included in header? Is there any difference ValidateAntiForgeryToken in ASP.NET Core vs ASP.NET MVC 5?

Upvotes: 0

Views: 7739

Answers (2)

LP13
LP13

Reputation: 34079

To solve my issue i have implemented custom ValidateAntiForgeryToken. I have decided to include token in header.

  1. I have several ajax POST method throughout application,So instead of adding it on each action method i want to validate it globally.
  2. On client side i want to include the token implicitly for each ajax POST request. (application also has kendo grids which makes ajax post request to get data)
  3. ajax post data could be json object or serialized form, which makes adding __RequestVerificationToken to data little bit tricky (not impossible), especially when i want to handle it globally.

So here is my complete code

Filter

public class ValidatePostAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
    {
        private const string _tokenKey = "__RequestVerificationToken";
        public void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }

            if (filterContext.HttpContext.Request.HttpMethod.ToUpper() == "POST")
            {
                if (filterContext.HttpContext.Request.IsAjaxRequest())
                {
                    if (filterContext.HttpContext.Request.Cookies.Get(_tokenKey) == null ||
                    filterContext.HttpContext.Request.Headers.Get(_tokenKey) == null)
                    {
                        throw new HttpPostAntiForgeryException("Invalid Verification Token.");
                    }

                    AntiForgery.Validate(filterContext.HttpContext.Request.Cookies[_tokenKey].Value, filterContext.HttpContext.Request.Headers[_tokenKey]);
                }                
            }
        }
    }

Exception

public sealed class HttpPostAntiForgeryException : HttpException
{
    public HttpPostAntiForgeryException()
    {
    }
    public HttpPostAntiForgeryException(string message) : base(message)
    {
    }
}

Add filter globally

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        filters.Add(new AuthorizeAttribute() { Order = 0 });
        filters.Add(new ValidatePostAntiForgeryTokenAttribute() { Order = 1 });
    }
}

_layout.cshtml - js to add token

 $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
                if (originalOptions.type === "POST")
                    jqXHR.setRequestHeader('__RequestVerificationToken', $('input[name="__RequestVerificationToken"]').val());
            });

Add token in _layout.cshtml

  @Html.AntiForgeryToken()

Upvotes: 1

Kaushal
Kaushal

Reputation: 1339

To fix your problem, everything remains same, except for setRequestHeader. Pass __RequestVerificationToken in request body

data: {
                __RequestVerificationToken: $('input[name="__RequestVerificationToken"]').val(),
                // Other properties of data
            },

Upvotes: 1

Related Questions