I'm using IdentityServer4 with ASP.NET Core 2.2. On the Post Login method I have applied the ValidateAntiForgeryToken. Generally after 20 minutes to 2 hours of sitting on the login page and then attempting to login it produces a blank page.
If you look at Postman Console you get a 400 Bad Request message. I then set the Cookie Expiration on the AntiForgery options to 90 days. I was able to allow the page to sit for up to 6 hours and still login. However, after around 8 hours (overnight), I received the blank page again after attempting to login.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login
services.AddAntiforgery(options =>
{
options.Cookie.Expiration = TimeSpan.FromDays(90);
});
I expect to be able to sit on the login page for 90 days which is the duration of the cookie but that doesn't work. How do I get the cookie for the AntiforgeryToken to last the entire 90 days or whatever time I set it to and not timeout or expire? Is there a way to catch this error and redirect the user back to the login method?
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!
AntiForgeryToken()Generates a hidden form field (anti-forgery token) that is validated when the form is submitted.
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 configuration specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster.
Since ASP.Net Core 3.0 MS decided to make ValidateAntiforgeryTokenAuthorizationFilter
internal. Now we have to copy-paste their code, to be able to derive. But most likely we don't need to. To just change the resulting behavior all we need is to test the context for the IAntiforgeryValidationFailedResult
and proceed accordantly, as described in this example.
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Core.Infrastructure;
using Microsoft.AspNetCore.Mvc.Filters;
namespace BasicWebSite.Filters
{
public class RedirectAntiforgeryValidationFailedResultFilter : IAlwaysRunResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is IAntiforgeryValidationFailedResult result)
{
context.Result =
new RedirectResult("http://example.com/antiforgery-redirect");
}
}
public void OnResultExecuted(ResultExecutedContext context)
{ }
}
}
Then within the controller:
// POST: /Antiforgery/LoginWithRedirectResultFilter
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
[TypeFilter(typeof(RedirectAntiforgeryValidationFailedResultFilter))]
public string LoginWithRedirectResultFilter(LoginViewModel model)
{
return "Ok";
}
Yet another implementation using the default one including all prechecks, logging etc. And it's still an AuthorizationFilter
, so that prevents any further action execution. The only difference is that it triggers HttpGet
to the same url instead of the default 400 response, a kind of the Post/Redirect/Get pattern implementation.
public class AnotherAntiForgeryTokenAttribute : TypeFilterAttribute
{
public AnotherAntiForgeryTokenAttribute() : base(typeof(AnotherAntiforgeryFilter))
{
}
}
public class AnotherAntiforgeryFilter:ValidateAntiforgeryTokenAuthorizationFilter,
IAsyncAuthorizationFilter
{
public AnotherAntiforgeryFilter(IAntiforgery a, ILoggerFactory l) : base(a, l)
{
}
async Task IAsyncAuthorizationFilter.OnAuthorizationAsync(
AuthorizationFilterContext ctx)
{
await base.OnAuthorizationAsync(ctx);
if (ctx.Result is IAntiforgeryValidationFailedResult)
{
// the next four rows are optional, just illustrating a way
// to save some sensitive data such as initial query
// the form has to support that
var request = ctx.HttpContext.Request;
var url = request.Path.ToUriComponent();
if (request.Form?["ReturnUrl"].Count > 0)
url = $"{url}?ReturnUrl={Uri.EscapeDataString(request.Form?["ReturnUrl"])}";
// and the following is the only real customization
ctx.Result = new LocalRedirectResult(url);
}
}
}
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