Reputation: 34079
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
Reputation: 34079
To solve my issue i have implemented custom ValidateAntiForgeryToken. I have decided to include token in header.
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
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