2

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:

  1. 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;
            },
    
  2. 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.

H. Pauwelyn
  • 11,346
  • 26
  • 71
  • 115
thiag0
  • 2,149
  • 5
  • 29
  • 50

2 Answers2

1

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>
Community
  • 1
  • 1
John Mc
  • 2,761
  • 1
  • 20
  • 34
  • Thanks for your response. We have a custom attribute which validates AJAX requests via the header. Please see updated post. – thiag0 Apr 29 '16 at 15:43
  • Here is my step-by-step approach on this issue. I am using angularJS, jquery, ASP.NET MVC 5 https://stackoverflow.com/a/57781976/2508781 – jitin14 Sep 04 '19 at 05:30
0

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.

thiag0
  • 2,149
  • 5
  • 29
  • 50