I need to reload an AntiForgeryToken in a form located in a view, after a successfull login in another view in the same page.
Can I make an update in the form input @Html.AntiForgeryToken() key with the new one from the result login page via jQuery?
If yes, is this recomended and secure?
How can I do it?
EDIT:
In the Layout I have diferent PartialViews:
The Partial to login:
<ul class="menudrt" id="headerLogin">
@{ Html.RenderAction(MVC.Account.LoginHeader()); }
</ul>
And in another Partial, the hability to send a comment:
<div class="comentsform">
<!-- Comments form -->
@{ Html.RenderAction(MVC.Comment.Create()); }
</div>
To send a comment, the user have to login, so after login, the comment form needs to update the AntiForgeryToken or I get the validation error because it's diferent now that the login has been made.
Thanks
This cookie is used to store the antiforgery token value in the client side, so clients can read it and sends the value as the HTTP header. Default cookie name is XSRF-TOKEN , expiration time is 10 years (yes, ten years!
ASP.NET Core uses a hidden field to store the anti-forgery token and uses the ValidateAntiForgeryToken attribute to validate the token. As the token is sent to the browser in a hidden field, it is also stored in an HttpOnly cookie.
Anti-Forgery TokensOne token is sent as a cookie. The other is placed in a hidden form field. The tokens are generated randomly so that an adversary cannot guess the values. When the client submits the form, it must send both tokens back to the server.
Error: The anti-forgery token could not be decrypted. If this application is hosted by a Web Farm or cluster, ensure that all machines are running the same version of ASP.NET Web Pages and that the <machineKey> configuration specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster.
The issue is occurring because the AntiForgery token contains the username of the currently authenticated user.
So here's what happens:
So you have a couple of options to fix this issue:
The obviousness of solution 1. doesn't make it a good candidate for covering it in my answer. Let's see how the second solution could be implemented.
But first let's reproduce the problem with an example:
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login()
{
FormsAuthentication.SetAuthCookie("john", false);
return Json(new { success = true });
}
[HttpPost]
[ValidateAntiForgeryToken()]
public ActionResult Comment()
{
return Content("Thanks for commenting");
}
}
~/Views/Home/Index.cshtml
:
<div>
@{ Html.RenderPartial("_Login"); }
</div>
<div id="comment">
@{ Html.RenderPartial("_Comment"); }
</div>
<script type="text/javascript">
$('#loginForm').submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
alert('You are now successfully logged in');
}
});
return false;
});
</script>
~/Views/Home/_Login.cshtml
:
@using (Html.BeginForm("Login", null, FormMethod.Post, new { id = "loginForm" }))
{
@Html.AntiForgeryToken()
<button type="submit">Login</button>
}
~/Views/Home/_Comment.cshtml
:
@using (Html.BeginForm("Comment", null, FormMethod.Post))
{
@Html.AntiForgeryToken()
<button type="submit">Comment</button>
}
Alright now when you navigate to the Home/Index the corresponding view will be rendered and if you press on the Comment button without logging-in first it will work. But if you login and then Comment it will fail.
So we could add another controller action that will return a partial view with a simple Html.AntiForgeryToken
call in order to generate a fresh token:
public ActionResult RefreshToken()
{
return PartialView("_AntiForgeryToken");
}
and the corresponding partial (~/Views/Home/_AntiForgeryToken.cshtml
):
@Html.AntiForgeryToken()
And the final step is to refresh the token by updating our AJAX call:
<script type="text/javascript">
$('#loginForm').submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
$.get('@Url.Action("RefreshToken")', function (html) {
var tokenValue = $('<div />').html(html).find('input[type="hidden"]').val();
$('#comment input[type="hidden"]').val(tokenValue);
alert('You are now successfully logged in and can comment');
});
}
});
return false;
});
</script>
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