This beautiful abstraction lets you place @Html.AntiForgeryToken()
in cshtml file which is magically expanded to something like;
<input name="__RequestVerificationToken" type="hidden" value="JjMHm5KJQ/qJsyC4sgifQWWX/WmADmNvEgHZXXuB07bWoL84DrmQzE6k9irVyFSJ5VSYqeUIXgl4Dw4NHSotLwflGYTyECzLvrgzbtonxJ9m3GVPgUV7Z6s2Ih/klUB78GN7Fl4Gj7kxg62MEoGcZw175eVwTmkKJ0XrtEfD5KCVvYIMHNY8MT2l+qhltsGL87c9dII42AVoUUQ2gTvfPg==" />
By mvc before the page is served. However my page has some JavaScript making ajax calls which don't include the token even though it's been added to the form. They are currently getting the expected [HttpAntiForgeryException]: A required anti-forgery token was not supplied or was invalid.
because they don't have the token. I'm aware I could parse the value out of the DOM but I shouldn't have to. Are there other ways of accessing/getting this value? To be clear, I mean I'd like an overload of the method that returns just the value as a string or some kind of object that has the name and value both.
To provide a bit more context my form and the relevant JS looks a little like this;
<form action="/settings" method="post"><input name="__RequestVerificationToken" type="hidden" value="JjMHm5KJQ/qJsyC4sgifQWWX/WmADmNvEgHZXXuB07bWoL84DrmQzE6k9irVyFSJ5VSYqeUIXgl4Dw4NHSotLwflGYTyECzLvrgzbtonxJ9m3GVPgUV7Z6s2Ih/klUB78GN7Fl4Gj7kxg62MEoGcZw175eVwTmkKJ0XrtEfD5KCVvYIMHNY8MT2l+qhltsGL87c9dII42AVoUUQ2gTvfPg==" /> <fieldset>
<h3>User Settings</h3>
<ul>
<li>
label for="password">Password</label>
<a href="#" id="change_password" class="changePasswordButton">Edit</a>
<div id="password_section" class="inlineedit">
<div>
<span for="existing_password">Current password</span> <input autocomplete="off" class="required" id="existing_password" name="existing_password" type="password" />
</div>
<div>
<span for="new_password">New password</span> <input autocomplete="off" class="required" id="new_password" name="new_password" type="password" />
<span id="password_strength" />
</div>
<div>
<span for="confirm_password">Confirm password</span> <input autocomplete="off" class="required" id="confirm_password" name="confirm_password" type="password" />
</div>
<div class="inlinesave">
<input type="button" value="Change" onclick="onPostChangePassword();"/>
<a href="#" id="cancel_password" class="cancel">Cancel</a>
</div>
</div>
</li>
// a bunch more of these that call their own onPostChangeSetting method
onPostChangePassword()
does some input validation then;
if(validPWD && validNewPWD && validConfirmPWD && current_pwd != new_pwd){
// Post the password change
var currentAjaxRequest = $.ajax({
type: "POST",
url: "/settings/preferences/changepassword",
cache: false,
data: {password: $('#new_password').val(), current: $('#existing_password').val(),confirm: $('#confirm_password').val()},
success: password_success,
error: password_error,
dataType: "json"
});
return true;
}
Which ideally (since this is verbatim in a cshtml file) would be modified with something like this;
data: {password: $('#new_password').val(), current: $('#existing_password').val(),confirm: $('#confirm_password').val(),
__RequestVerificationToken:@Html.AntiForgeryValue() }
tl;dr is there a way to interact with the AntiForgeyToken before it's turned into an string of html?
AntiForgeryToken()Generates a hidden form field (anti-forgery token) that is validated when the form is submitted.
TYPE. __RequestVerificationToken. www.grpgroup.co.uk. This is an anti-forgery cookie set by web applications built using ASP.NET MVC technologies. It is designed to stop unauthorised posting of content to a website, known as Cross-Site Request Forgery.
Try quick fixes The common “possible solutions” to anti-forgery token/cookie related issues are disabling output caching and enabling heuristic checks.
You can use code like this (in for example _Layout.cshtml) to add the AntiForgery header to all Ajax POST requests, or you could adapt it for a specific request. (Code assumes you are using jQuery)
@functions{
private static string TokenHeaderValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
<script type="text/javascript">
$(document).ajaxSend(function (event, jqxhr, settings) {
if (settings.type == "POST") {
jqxhr.setRequestHeader('@ValidateHttpAntiForgeryTokenAttribute.RequestVerificationTokenName',
'@TokenHeaderValue()');
}
});
</script>
On the server side for these Ajax calls, you then want to call the overload of AntiForgery.Validate that takes the cookie and form token, which you would enable by adding this attribute to the action methods called via Ajax (explicitly, or by parent controller, or via a filter)
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
AllowMultiple = false, Inherited = true)]
public sealed class ValidateHttpAntiForgeryTokenAttribute
: FilterAttribute, IAuthorizationFilter
{
public const string RequestVerificationTokenName = "RequestVerificationToken";
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
ValidateRequestHeader(filterContext.HttpContext.Request);
}
else
{
AntiForgery.Validate();
}
}
private static void ValidateRequestHeader(HttpRequestBase request)
{
string cookieToken = string.Empty;
string formToken = string.Empty;
var tokenValue = request.Headers[RequestVerificationTokenName];
if (string.IsNullOrEmpty(tokenValue) == false)
{
string[] tokens = tokenValue.Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With