Reputation: 2229
I'm having some issues passing the correct AntiForgertyToken when using AngularJS $http service in an ASP.NET MVC application.
I have tried the following:
Setting HTTP Request headers with an httpInterceptor
app.factory('httpInterceptorService', function ($q) {
return {
'request': function (config) {
blockUI();
config.headers['If-Modified-Since'] = 'Mon, 26 Jul 1997 05:00:00 GMT'; // Disables IE AJAX request caching
config.headers['Cache-Control'] = 'no-cache';
config.headers['Pragma'] = 'no-cache';
config.headers['X-Requested-With'] = 'XMLHttpRequest';
config.headers['__RequestVerificationToken'] = $('[name=__RequestVerificationToken]').val();
return config;
},
Setting HTTP Request headers via a factory service
app.factory('networkService', function ($http) {
return {
postDataAsAjax: function (url, params) {
debugger;
return $http({
method: 'POST',
url: url,
data: params,
headers: {
'__RequestVerificationToken': $('[name=__RequestVerificationToken]').val(),
'X-Requested-With': 'XMLHttpRequest'
}
}).then(function (result) {
Both of these approaches are throwing AntiForgeryTokenException.
Is there any other method that I can achieve this?
EDIT (Added HTTP Request Info)
POST /WebApplication1/Home/Index HTTP/1.1
Host: localhost
Connection: keep-alive
Content-Length: 799
Cache-Control: no-cache
Pragma: no-cache
Origin: http://localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36
Content-Type: application/json;charset=UTF-8
Accept: application/json, text/plain, */*
X-Requested-With: XMLHttpRequest
If-Modified-Since: Mon, 26 Jul 1997 05:00:00 GMT
__RequestVerificationToken: CKCARSoIug5mHnHmUT4ciSmf3pCk1YJkcwq3czo5snfEwTVPBUYLQj7z7w3KKDu001RYk7zuMZ1LEwwWB1tNpZR0agxJK1DjqjMDnQNewLKGCmExANXIJ-Du7lc0LEFw0
Referer: http://localhost/SSP-Working_SourceCode/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: __ngDebug=true; ASP.NET_SessionId=4kcpxz1oj0042ndw4aotx0jl; __RequestVerificationToken_L1NTUC1Xb3JraW5nX1NvdXJjZUNvZGU1=Q8FOz7jJHQHdes02AJvRGglFU_pcz5eqcnZY3QXg37z9k1LMYiPWq-kKbXlYCbAfK0IgLpCtpBax6w-rB1J_NBi7KzGyCuwLCHjKNREjMhQ1; .ASPXFORMSAUTH=CC35114F38FD17866FAF38A1FDC525263A0858EFECFB03AEEED7E9AF7FAA2995262A426D4AA50EB87C47969C3C191BC9B3D31BC67A831C099F286AD3013348B14659632BC54425E3D81C19CB382E175B2DA3755DDFE46D7A79810FB79EBE832D616A299C93CFDA2105576B922C6A1D111A23BB6F9594532C310A15AF2162785A
EDIT (Added custom anti forgery token attribute)
public class GlobalAntiForgeryTokenAttribute : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext authorizationContext)
{
var request = authorizationContext.HttpContext.Request;
if (request.HttpMethod.ToUpper() != "POST")
{
return;
}
if (authorizationContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true) ||
authorizationContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true))
{
return;
}
if (request.IsAjaxRequest())
{
var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];
var cookieValue = antiForgeryCookie != null ? antiForgeryCookie.Value : null;
AntiForgery.Validate(cookieValue, request.Headers["__RequestVerificationToken"]);
}
else
{
new ValidateAntiForgeryTokenAttribute().OnAuthorization(authorizationContext);
}
}
}
EDIT (Added HTTP Response)
System.Web.Mvc.HttpAntiForgeryException
: The required anti-forgery form field__RequestVerificationToken
is not present.
Upvotes: 2
Views: 3448
Reputation: 2229
For those who are facing a similar issue, the problem with our application was that the previous developer had also defined the default "ValidateAntiForgeryToken" attribute on the MVC controller action the requeset was POSTing to.
Once we removed [ValidationAntiForgeryToken] from the controller action, it started working.
We have a custom anti forgery token attribute which checks for the token in the header, but the default implementation of the MVC attribute only checks the request body, which was causing this to fail.
Upvotes: 0
Reputation: 2933
The problem is that the verification token is meant to be part of the form data but you're providing it in the header.
This post tells you how to build an attribute filter that validates the header instead.
Here is mine:
[AttributeUsage(AttributeTargets.Class)]
public class ValidateAntiForgeryTokenOnAjax : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var request = filterContext.HttpContext.Request;
// Only validate POSTs
if (request.HttpMethod == WebRequestMethods.Http.Post)
{
// Ajax POSTs and normal form posts have to be treated differently when it comes
// to validating the AntiForgeryToken
if (request.IsAjaxRequest())
{
var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];
var cookieValue = antiForgeryCookie != null
? antiForgeryCookie.Value
: null;
AntiForgery.Validate(cookieValue, request.Headers[AntiForgeryConfig.CookieName]);
}
else
{
new ValidateAntiForgeryTokenAttribute()
.OnAuthorization(filterContext);
}
}
}
I then configure angular like this:
var myApp = angular.module("myApp", ["ngRoute"])
.run(function ($http) {
$http.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
$http.defaults.headers.post["__RequestVerificationToken"] = $("#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]").val();
});
On my master layout page I declare this form:
<form id="__AjaxAntiForgeryForm" action="#" method="post">@Html.AntiForgeryToken()</form>
Upvotes: 1