Reputation: 860
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
Upvotes: 0
Views: 7888
Reputation: 142
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!
Upvotes: 1
Reputation: 13498
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
.
Upvotes: 1