Search code examples
c#asp.net-core-mvcintegration-testingantiforgerytoken

How to bypass antiforgery token validation on form post in integration tests


I've an ASP.NET core 2.2 MVC app which exposes a Login page with a basic form (username/password). The controller action is protected by the AntiForgeryTokenAttribute, and the hidden __RequestVerificationToken is added by MVC.

I'm writing Integration Tests using TestServer and I want to send the form and see if I get 302 status code, but I couldn't find any valid option.

One option I evaluated was to do the GET, extract the __RequestVerificationToken and then submit the tpoken as part of the form. Howerer this won't work as I am missing the cookie (I believe). TestServer.CreateClient doesn't support any handler, so I can't add cookies.

Is there a way to test this ?

Thanks!


Solution

  • so there are two things needed:

    1. during the page GET: get the cookie from the Headers and extract the __RequestVerification
    2. when submitting the form: add the cookie in the Headers and add __RequestVerification to the model

    1. GET

    You can extract the token using:

    headers.FirstOrDefault(x => x.Key == "Set-Cookie").Value.First().Split(" ")[0];
    
    // The cookie we are looking for is .AspNetCore.Antiforgery.<unique guid>=<unique guid>
    var tokenCookie = cookieContent.Split("=");
    var name = tokenCookie[0];
    var value = tokenCookie[1];
    

    You can extract the __RequestVerification using Nuget package HtmlAgilityPack and then doing:

    var htmlDoc = new HtmlDocument();
    htmlDoc.LoadHtml(htmlContent);
    var tokenValue = htmlDoc.DocumentNode.SelectSingleNode("//input[@name='__RequestVerificationToken']")
                    .Attributes.Where(x => x.Name == "value").Select(x => x.Value).First();
    

    where htmlContent is HttpResponse.Content.ReadAsStringAsync();

    2. POST

    When you create the form, add the __RequestVerificationToken:

    new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
    {
        ... your stuff here
        new KeyValuePair<string, string>("__RequestVerificationToken", token)
            });
    }
    

    then when sending the request:

    var request = new HttpRequestMessage(HttpMethod.Post, endPoint)
    { Content = form };
    
    request.Headers.Add("Cookie", $"{cookie.Name}={cookie.Value}");
    
    await client.SendAsync(request);
    

    where client is HttpClient created using TestServer.CreateClient.

    Hope this helps somebody else!