/antiforgery-testing-application-part

Sample application for integration testing ASP.NET Core applications using antiforgery protections

Primary LanguageC#Apache License 2.0Apache-2.0

ASP.NET Core Application Part For Testing Anti-forgery Protected Resources

Build status

Introduction

An example application that demonstrates using ASP.NET Core Application Parts for easier integration testing of HTTP resources that are protected by the anti-forgery features of ASP.NET Core, such as [ValidateAntiforgeryToken].

How It Works

To avoid the danger of having unsafe code that returns valid CSRF tokens in the application itself, instead we can use Application Parts to inject additional functionality into the server at runtime when running integration tests using WebApplicationFactory<TEntryPoint>.

The test project contains the AntiforgeryTokenController. This contains an HTTP GET resource that uses the Antiforgery features to return a JSON payload containing valid CSRF tokens and the relevant cookie/form/header names to validate requests:

public IActionResult GetAntiforgeryTokens(
    [FromServices] IAntiforgery antiforgery,
    [FromServices] IOptions<AntiforgeryOptions> options)
{
    AntiforgeryTokenSet tokens = antiforgery.GetTokens(HttpContext);

    var model = new AntiforgeryTokens()
    {
        CookieName = options.Value.Cookie.Name,
        CookieValue = tokens.CookieToken,
        FormFieldName = options.Value.FormFieldName,
        HeaderName = tokens.HeaderName,
        RequestToken = tokens.RequestToken,
    };

    return Json(model);
}

This is then configured as an Application Part by the ConfigureAntiforgeryTokenResource() method, which is registered with the test server:

protected override void ConfigureWebHost(IWebHostBuilder builder)
{
    builder.ConfigureAntiforgeryTokenResource();
}

This then allows tests to use the GetAntiforgeryTokensAsync() to perform an HTTP GET to the application to obtain valid CSRF tokens to use:

public async Task<AntiforgeryTokens> GetAntiforgeryTokensAsync()
{
    using var httpClient = CreateClient();
    using var response = await httpClient.GetAsync(AntiforgeryTokenController.GetTokensUri);
    return JsonSerializer.Deserialize<AntiforgeryTokens>(await response.Content.ReadAsStringAsync());
}

The tests then use this to configure an HttpClient with CSRF tokens so that HTTP POST/DELETE etc. requests to the application pass the checks by the antiforgery protections.

[Fact]
public async Task Can_Create_Todo_Item_With_Html_Form()
{
    // Arrange - Get valid CSRF tokens and parameter names from the server
    AntiforgeryTokens tokens = await Fixture.GetAntiforgeryTokensAsync();

    // Configure a handler with the CSRF cookie
    using var cookieHandler = new CookieContainerHandler();
    cookieHandler.Container.Add(
        Fixture.Server.BaseAddress,
        new Cookie(tokens.CookieName, tokens.CookieValue));

    // Create an HTTP client and add the CSRF cookie
    using var httpClient = Fixture.CreateDefaultClient(cookieHandler);

    // Create form content to create a new item with the CSRF parameter added
    var form = new Dictionary<string, string>()
    {
        [tokens.FormFieldName] = tokens.RequestToken,
        ["text"] = "Buy milk",
    };

    // Act - Create a new item
    using var content = new FormUrlEncodedContent(form);
    using var response = await httpClient.PostAsync("home/additem", content);

    // Assert - The item was created
    response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
}

Feedback

Any feedback or issues can be added to the issues for this project in GitHub.

Repository

The repository is hosted in GitHub: https://github.com/martincostello/antiforgery-testing-application-part.git

License

This project is licensed under the Apache 2.0 license.

Building and Testing

Compiling the application yourself requires Git and the .NET SDK to be installed.

To build and test the application locally from a terminal/command-line, run the following set of commands:

git clone https://github.com/martincostello/antiforgery-testing-application-part.git
cd antiforgery-testing-application-part
./build.ps1