I am trying to implement AntiForgeryToken for my MVC3 Application. I am having a problem with AntiForgeryToken after setting FormAuthentication cookie. Here is a simple example which explains my problem.
I have home controller with following action methods:
public class HomeController : Controller
{
public ActionResult Logon()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Logon(string userName, string password)
{
FormsAuthentication.SetAuthCookie(userName, false);
return View("About");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult About(FormCollection form)
{
return View("PageA");
}
}
And here is my Logon and About views:
Logon.cshtml:
@using (Html.BeginForm("Logon", "Home"))
{
@Html.AntiForgeryToken()
<label> UserName :</label>
<input name = "userName" type="text"/>
<br />
<label> Password :</label>
<input name = "password" type="password"/>
<br />
<br />
<input type="submit" value="LogOn" />
}
About.cshtml
@using (Html.BeginForm("About", "Home"))
{
@Html.AntiForgeryToken()
<p> This is conent of page About</p>
<input name = "moreInfo" type="text"/>
<input type="submit" value="SubmitAbout" />
}
I have no problem on "Logon" post method. It is validating the antiforgerytoken and rendering About view. Interestingly, when I do post on "About" view I am getting error "A required anti-forgery token was not supplied or was invalid"
could some one point out what I am doing wrong here?
Appreciate your help.
The AntiForgeryToken Helper does not add any cookie to response, if a cookie with same name exist in the request. Also the AntiForgeryToken Helper uses Principal.Identity.Name to return a value for hidden field.
So when your Login view uses Html.AntiForgeryToken, a new cookie is set on response and a hidden field with same value. When your Login view post this cookie with hidden field, no exception will be thrown because both request cookie and hidden field value matches. But in the case of About view, no additional cookie will be added to response, but due to IIdentty, a new hidden value will be return for helper. So when you post About action, an exception will raise because cookie and hidden value does not match.
This may be a bug in AntiForgeryToken implementation.
I did some tests, and determined that even after you call
FormsAuthentication.SetAuthCookie(...)
, the problem is thathttpContext.User.Identity.Name
will still be empty for the duration of the request.Therefore, to solve this issue, you need to manually set the current
User
as so:This will set the correct
User
that is used whenHtml.AntiForgeryToken()
is called.Please note that this code isn't necessary for normal PRG-pattern websites, because after the redirect, the correct
User
will be loaded.Also, since your
Logon
method requires a valid user name and password, it isn't really susceptible to CSRF attacks, so you probably don't need to useValidateAntiForgeryToken
on that method. Maybe that's why the AntiForgeryToken is dependent on the user name. CSRF attacks usually only exploit already-authenticated users.I seem to recall once you login your token is now different as your username I believe changes this token, hence would no longer be valid. I'll try to double check this but almost certain I ran into this in the past.
However in your code above you will run up against other issues if you use this pattern. Post actions aren't generally meant to display a view unless there has been an exception/validation error and you are redisplaying the page. Generally you would redirect. I see someone touched upon this in a comment above and they are correct.
This doesn't mean you shouldn't use those actions though but beware of crossing this over a login. This prior post alludes to the use of username with tokens:
Troubleshooting anti-forgery token problems