1

I am working on an ASP.NET Core 2.0 Web API and I want to do some integration tests using ASP.NET Core's TestServer class. I am using xUnit as my testing framework so I have created a TestServerFixture class that creates the in-memory TestServer instance and then use the TestServer's .CreateClient() to create the HTTPClient instance.

My Web API requires an OAuth2.0 Access Token from my Azure AD. I set this up using this code in my Startup.cs, ConfigureServices method:

        // Add Azure AD OAUTH2.0 Authentication Services
        services.AddAuthentication(sharedOptions =>
        {
            sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddAzureAdBearer(options => Configuration.Bind("AzureAd", options));

and in my controllers, I have the [Authorize] attribute on the class.

So for my Integration Tests setup, I have a method in my TestServerFixture that obtains a valid token from Azure AD and I add it to my client request header as follows;

Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await _testServerFixture.GetAccessToken());

When I debug my integration test, I can see that the request does contain a valid access token but I am still getting a 401 Unauthorized from the API when I run my Integration Test.

After doing some digging I found several resources that talk about a similar issue with TestServer, but related to Authentication rather than Authorization, as I am experiencing. Here are links to these resources;

https://medium.com/@zbartl/authentication-and-asp-net-core-integration-testing-using-testserver-15d47b03045a

How do I integration test a ASP 5/Core Web API with [Authorize] Attributes

http://geeklearning.io/how-to-deal-with-identity-when-testing-an-asp-net-core-application/

These all talk about assigning a ClaimsPrincipal to the context.user using custom middleware. Since this is based upon Authentication rather than Authorization, I am not sure if I can do something similar for my Access Token.

I do know that in my API, I can access the HTTPContext.User and pull out the AppId value, which is part of the Access Token so it would seem that Authentication and Authorization both use the Context.User.

So, before I burn time building up my own custom middleware for this purpose, I wanted to see if anyone has already addressed this issue or perhaps are aware of a NuGet that does what I need.

EDIT - SOLUTION

I am showing this in case anyone else runs into this issue.

I ended up building the middleware that Zach Bartlett presented in his blog , but making the following changes.

public class AuthenticatedTestRequestMiddleware
{

    #region Class Variables

    private const string TestingAccessTokenAuthentication = "TestingAccessTokenAuthentication";
    private readonly RequestDelegate _next;

    #endregion Class Variables

    #region Constructor(s)

    public AuthenticatedTestRequestMiddleware(RequestDelegate next)
    {
        _next = next;
    }


    #endregion Constructor(s)

    public async Task Invoke(HttpContext context)
    {
        if (context.Request.Headers.Keys.Contains("X-Integration-Testing"))
        {

            if (context.Request.Headers.Keys.Contains("Authorization"))
            {
                var token = context.Request.Headers["Authorization"].First();

                var claimsIdentity = new ClaimsIdentity(new List<Claim>
                {
                    new Claim(ClaimTypes.Authentication, token)
                }, TestingAccessTokenAuthentication);

                var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
                context.User = claimsPrincipal;
            }
        }

        await _next(context);
    }

}

There were one interesting "Gotcha".

In Zach's blog he had the code;

public const string TestingHeader = "X-Integration-Testing";

at the top of his middleware and then references the TestingHeader in the test for the key in the header collection like this;

if (context.Request.Headers.Keys.Contains(TestingHeader)  

Doing it this way was failing for me until I put the string literal instead of the variable into the .Contains() clause.

Now, my integration test is passing with a 200 OK response. :)

2
  • 1
    I trust your AuthenticatedTestRequestMiddleware middleware is not added to the request pipeline unless it's running in Test Mode? Otherwise you're adding a gaping security hole to your application. Commented Feb 17, 2019 at 5:06
  • More answers here: stackoverflow.com/questions/41112564/… Commented Apr 6, 2021 at 15:26

1 Answer 1

0

I was able to find a solution following Zach Bartlett's blog post, and making some small changes to make it pertain to the Authentication header. The code is shown as an edit in my original post above.

Sign up to request clarification or add additional context in comments.

1 Comment

can you provide detailed text fixture class or code if possible ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.