Here is the scenario:
I have a login page, when user sign it it is redirected to home application page. Then user is using browser back button, and now he is on login page. He tries to login again but now an exception is thrown:
HttpAntiForgeryException (0x80004005): The provided anti-forgery token was meant for user "", but the current user is "userName".
I know this is related to caching. I disabled browser caching for login action using custom NoCache filter which sets all required headers - no-cache, no-store, must-revalidate, etc. But
I will try to make hacks and force safari mobile to refresh, but this is not what I'm expecting.
I would like to know if I can:
UPDATE 1
To make some clarification, I know how to handle errors in MVC. The problem is that this handling errors is not solving my problem at all. Basic idea of error handling is redirect to custom error page with nice message. But I want to prevent this error to happen, not to handle it in user visible way. By handle I mean catch make username replace or other suitable action then continue login.
UPDATE 2
I've added below solution which is working for me.
After some time of investigation I think I found some way how to get rid of this error for user. It is not perfect but at least not display error page:
I created filter based on HandleErrorAttribute
:
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes",
Justification = "This attribute is AllowMultiple = true and users might want to override behavior.")]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class LoginAntiforgeryHandleErrorAttribute : FilterAttribute, IExceptionFilter
{
#region Implemented Interfaces
#region IExceptionFilter
/// <summary>
/// </summary>
/// <param name="filterContext">
/// The filter context.
/// </param>
/// <exception cref="ArgumentNullException">
/// </exception>
public virtual void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.IsChildAction)
{
return;
}
// If custom errors are disabled, we need to let the normal ASP.NET exception handler
// execute so that the user can see useful debugging information.
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
{
return;
}
Exception exception = filterContext.Exception;
// If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
// ignore it.
if (new HttpException(null, exception).GetHttpCode() != 500)
{
return;
}
// check if antiforgery
if (!(exception is HttpAntiForgeryException))
{
return;
}
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "action", "Index" },
{ "controller", "Home" }
});
filterContext.ExceptionHandled = true;
}
#endregion
#endregion
}
Then I applied this filter to Login POST action:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
[LoginAntiforgeryHandleError]
public ActionResult Login(Login model, string returnUrl)
{
The main idea of this solution is to redirect anti-forgery exception to main index action. If user will still won't be unauthenticated it will show then login page if user will be already authenticated it will show index page.
UPDATE 1 There is one potential problem with this solution. If somebody is logging in with different credentials then on error it should be added additional login runtime - logout previous user and login new one. This scenario is not handled.