0

I'm unable to pass the RequestVerificationToken from webpage to server using AngularJs.

My AngularJs Code is:

var app = angular.module('validation', []);
app.controller('SignUpController', function ($scope, $http) {
    $scope.model = {};
    $scope.email = {};
    $scope.sendEmail = function () {
        $http({
            method: 'POST',
            url: '/Contact/Test',
            data: $scope.email,
            headers: {
                'RequestVerificationToken': $scope.antiForgeryToken
            }
        }).success();
    };
});

Custom Attribute Code:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class CustomAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
    {


        private void ValidateRequestHeader(HttpRequestBase request)
        {
            string cookieToken = String.Empty;
            string formToken = String.Empty;
            string tokenValue = request.Headers["RequestVerificationToken"];
            if (!String.IsNullOrEmpty(tokenValue))
            {
                string[] tokens = tokenValue.Split(':');
                if (tokens.Length == 2)
                {
                    cookieToken = tokens[0].Trim();
                    formToken = tokens[1].Trim();
                }
            }
            AntiForgery.Validate(cookieToken, formToken);
        }

        public void OnAuthorization(AuthorizationContext filterContext)
        {

            try
            {
                if (filterContext.HttpContext.Request.IsAjaxRequest())
                {
                    ValidateRequestHeader(filterContext.HttpContext.Request);
                }
                else
                {
                    AntiForgery.Validate();
                }
            }
            catch (HttpAntiForgeryException e)
            {
                throw new HttpAntiForgeryException("Anti forgery token cookie not found");
            }
        }
    }

Form is:

@functions{
    public string GetAntiForgeryToken()
    {
        string cookieToken, formToken;
        AntiForgery.GetTokens(null, out cookieToken, out formToken);
        return cookieToken + ":" + formToken;
    }
}
<div ng-app="validation" ng-controller="SignUpController">
    <form role="form" id="frmContact" action="@Url.Action("Index", "Contact")" method="POST">
        <input id="antiForgeryToken" ng-model="antiForgeryToken" type="hidden" ng-init="antiForgeryToken='@GetAntiForgeryToken()'" />
        <fieldset class="form-group">
            @Html.LabelFor(x => x.EmailTitle)
            @Html.TextBoxFor(x => x.EmailTitle, new { placeholder = @Resource.EmailTitle, @class = "form-control", data_ng_model = "new.email.title" })
        </fieldset>
        <fieldset class="form-group">
            @Html.LabelFor(x => x.EmailAddress)
            @Html.TextBoxFor(x => x.EmailAddress, new { placeholder = @Resource.EmailAddress, @class = "form-control", data_ng_model = "new.email.address" })
        </fieldset>
        <fieldset class="form-group">
            @Html.LabelFor(x => x.EmailMessage)
            @Html.TextAreaFor(x => x.EmailMessage, new { placeholder = @Resource.EmailMessage, @class = "form-control", data_ng_model = "new.email.message" })
        </fieldset>


        <div>
            <button type="submit" name="btnEmailForm" id="btnEmailForm" class="btnLogin" ng-click="sendEmail()" value="sendMessage">@Resource.ContactFormSendMessageButton</button>
        </div>
        <div id="errorMessages" class="error">{{message}}</div>
    </form>
</div>

I have read the following posts, but cannot seem to solve the problem, and also took code from https://github.com/techbrij/angularjs-asp-net-mvc which works in that example but not in my MVC application:

http://techbrij.com/angularjs-antiforgerytoken-asp-net-mvc

https://parthivpandya.wordpress.com/2013/11/25/angularjs-and-antiforgerytoken-in-asp-net-mvc/

AngularJS Web Api AntiForgeryToken CSRF

http://bartwullems.blogspot.co.uk/2014/10/angularjs-and-aspnet-mvc-isajaxrequest.html

Where exactly to put the antiforgeryToken

http://www.ojdevelops.com/2016/01/using-antiforgerytokens-in-aspnet-mvc.html

Can anyone help with this problem

Community
  • 1
  • 1
George Phillipson
  • 830
  • 11
  • 39
  • Not clear: on btnEmailForm click you want to send your form to Index/Contact and simultaneously perform post request to /Contact/Test? And your custom attribute: `CustomAntiForgeryTokenAttribute` on which of this actions it applied? And also antiForgeryToken input not has attribute `name= '__RequestVerificationToken'` that is why it not goes to server. – Slava Utesinov Mar 24 '16 at 11:39
  • My mistake, getting frustrated as unable to solve this problem Index/Contact should be /Contact/Test – George Phillipson Mar 24 '16 at 11:47

2 Answers2

1

At this case you perform form submit and $scope.sendEmail operations and they may conflict one with another, to prevent this behavior you can use ng-submit directive. And also add attributes: name= '__RequestVerificationToken' and ng-value="antiForgeryToken" to corresponding input.

Slava Utesinov
  • 13,410
  • 2
  • 19
  • 26
1

Important: Default behavior of [ValidateAntiForgeryToken] expects __RequestVerificationToken token in form values. To send request to server in form values format one requires content-type to be set to application/x-www-form-urlencoded. But I didn't have this option unfortunately and my content-type was application/json. Hence I took this custom path.

Let me explain the approach I took which worked.

Step 1: Declare @Html.AntiForgeryToken() in your view (.cshtml) as shown below:

<form id="inputForm" name="inputForm" ng-submit="submit(broker)" novalidate>
        @Html.AntiForgeryToken()
        /* other controls of form */
</form>

Step 2: @Html.AntiForgeryToken() will render an hidden field which will hold the token value as:

<input name="__RequestVerificationToken" type="hidden" value="GvTcz2tTgHOS2KK_7jpHvPWEJPcbJmHIpSAlxY1">

Step 3: Create custom attribute for Anti-forgery token verification as

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]    
public class HbValidateAntiForgeryToken : FilterAttribute, IAuthorizationFilter, IExceptionFilter    
{    
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        try
        {
            var antiForgeryCookie = filterContext.HttpContext.Request.Cookies[AntiForgeryConfig.CookieName];
            AntiForgery.Validate(antiForgeryCookie != null ? antiForgeryCookie.Value : null,
                filterContext.HttpContext.Request.Headers["__RequestVerificationToken"]);                
        }
        catch (Exception ex)
        {                
            throw new SecurityException("Unauthorised access detected and blocked");
        }
    }

    public void OnException(ExceptionContext filterContext)
    {
        if (filterContext.Exception != null &&
            filterContext.Exception is System.Security.SecurityException)
        {
            filterContext.Result = new HttpUnauthorizedResult();
            // Handle error page scenario here
        }
    }
}

Step 4: Declare the above attribute wherever required (Only on HttpPost methods of controller. DO NOT declare on HttpGet)

[HttpPost]
[HbValidateAntiForgeryToken]
public JsonResult IsUsernameExists(string username)
{
}

Step 5: In AngularJS, in factory pass __RequestVerificationToken as header.

hbServices.factory('RegistrationService', ['$resource',
function ($resource) {
    return $resource(applicationPath + 'api/MyUserMembership/:dest', {}, {
        createNewUser: { method: 'POST', isArray: false, params: { dest: 'CreateNewUser' }, 
                         headers: { 
                             '__RequestVerificationToken': $('input[name="__RequestVerificationToken"]').val()
                            }
                        },
        isUsernameExists: { method: 'POST', isArray: false, params: { dest: 'IsUsernameExists' }, 
        headers: { 
            '__RequestVerificationToken': $('input[name="__RequestVerificationToken"]').val()
           }
       }
    });
}]);

Please note the way I am passing value of __RequestVerificationToken read from hidden field that was rendered by ASP.NET MVC's @Html.AntiForgeryToken().

My application was using jquery and has reference of jquery already so reading the value was easy. You can try other methods to read this value

Summary The AntiForgery.Validate() does the magic of validating the value of forgery token here and is wonderful so far. Hope this helps!

jitin14
  • 144
  • 5