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 l
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);
}
}
}
This was my final solution. I added a attribute using the IAntifogery dependency injection.
public class CustomValidationAttribute : ActionFilterAttribute
{
private IAntiforgery _antiForgery { get; }
public CustomValidationAttribute(IAntiforgery antiforgery)
{
_antiForgery = antiforgery;
}
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var isRequestValid = await this._antiForgery.IsRequestValidAsync(context.HttpContext);
if (!isRequestValid)
{
//Add Code here if token is not valid
return;
}
await next();
}
}
Add the attribute to your controller methods that also use [HttpPost]
[TypeFilter(typeof(CustomValidationAttribute))]
Slight modification to d_f code https://stackoverflow.com/a/56383473/841898 Instead of page redirect we just add error to ModelState. Then we display in model state summary.
public class CustomAntiForgeryTokenAttribute : TypeFilterAttribute
{
public CustomAntiForgeryTokenAttribute() : 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)
{
ctx.ModelState.AddModelError("Token", "Validation Token Expired. Please try again");
ctx.Result = null;
}
}
}