Allow custom handling of antiforgery failures
To enable custom handling of antiforgery validation failures, use an `AntiforgeryValidationFailedResult` which is just a `BadRequestResult` but allows to be identified explicitly inside always-running result filters using the `IAntiforgeryValidationFailedResult` marker interface.
This commit is contained in:
parent
8a183bb4f4
commit
ddbe0fef26
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.Core.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="BadRequestResult"/> used for antiforgery validation
|
||||
/// failures. Use <see cref="IAntiforgeryValidationFailedResult"/> to
|
||||
/// match for validation failures inside MVC result filters.
|
||||
/// </summary>
|
||||
public class AntiforgeryValidationFailedResult : BadRequestResult, IAntiforgeryValidationFailedResult
|
||||
{ }
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Core.Infrastructure
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an <see cref="IActionResult"/> that is used when the
|
||||
/// antiforgery validation failed. This can be matched inside MVC result
|
||||
/// filters to process the validation failure.
|
||||
/// </summary>
|
||||
public interface IAntiforgeryValidationFailedResult : IActionResult
|
||||
{ }
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|||
catch (AntiforgeryValidationException exception)
|
||||
{
|
||||
_logger.AntiforgeryTokenInvalid(exception.Message, exception);
|
||||
context.Result = new BadRequestResult();
|
||||
context.Result = new AntiforgeryValidationFailedResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ using System.Linq;
|
|||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Antiforgery;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||
|
|
@ -175,5 +174,32 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
var pragmaValue = Assert.Single(response.Headers.Pragma.ToArray());
|
||||
Assert.Equal("no-cache", pragmaValue.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RequestWithoutAntiforgeryToken_SendsBadRequest()
|
||||
{
|
||||
// Arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Antiforgery/Login");
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RequestWithoutAntiforgeryToken_ExecutesResultFilter()
|
||||
{
|
||||
// Arrange
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Antiforgery/LoginWithRedirectResultFilter");
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||
Assert.Equal("http://example.com/antiforgery-redirect", response.Headers.Location.AbsoluteUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -73,5 +73,29 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|||
// Assert
|
||||
antiforgery.Verify(a => a.ValidateRequestAsync(It.IsAny<HttpContext>()), Times.Never());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Filter_SetsFailureResult()
|
||||
{
|
||||
// Arrange
|
||||
var antiforgery = new Mock<IAntiforgery>(MockBehavior.Strict);
|
||||
antiforgery
|
||||
.Setup(a => a.ValidateRequestAsync(It.IsAny<HttpContext>()))
|
||||
.Throws(new AntiforgeryValidationException("Failed"))
|
||||
.Verifiable();
|
||||
|
||||
var filter = new ValidateAntiforgeryTokenAuthorizationFilter(antiforgery.Object, NullLoggerFactory.Instance);
|
||||
|
||||
var actionContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());
|
||||
actionContext.HttpContext.Request.Method = "POST";
|
||||
|
||||
var context = new AuthorizationFilterContext(actionContext, new[] { filter });
|
||||
|
||||
// Act
|
||||
await filter.OnAuthorizationAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.IsType<AntiforgeryValidationFailedResult>(context.Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using BasicWebSite.Filters;
|
||||
using BasicWebSite.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
|
@ -39,6 +40,16 @@ namespace BasicWebSite.Controllers
|
|||
return "OK";
|
||||
}
|
||||
|
||||
// POST: /Antiforgery/LoginWithRedirectResultFilter
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
[TypeFilter(typeof(RedirectAntiforgeryValidationFailedResultFilter))]
|
||||
public string LoginWithRedirectResultFilter(LoginViewModel model)
|
||||
{
|
||||
return "Ok";
|
||||
}
|
||||
|
||||
// GET: /Antiforgery/FlushAsyncLogin
|
||||
[AllowAnonymous]
|
||||
public ActionResult FlushAsyncLogin(string returnUrl = null)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
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)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue