Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authentication with Microsoft.Owin.Testing.TestServer for In-Memory Integration Tests

I've just moved to using OWIN\Katana for a web api project. It uses Windows Authentication. This seems to be working, but most of my integration tests have broken. They were previously just using an In-Memory HttpServer but I've changed to using Microsoft.Owin.Testing.TestServer. I've replaced something like this in my test setup:

        var config = new HttpConfiguration { IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always };
        config.EnableQuerySupport();
        Server = new HttpServer(config);
        MyConfigClass.Configure(config);
        WebApiConfig.Register(config);

with a simpler:

TestServer = TestServer.Create<Startup>();

But whereas previously I could just put the following to "fake" authentication with the in-memory server:

Thread.CurrentPrincipal = new ClientRolePrincipal(new HttpListenerBasicIdentity(Username, Password));

This now doesn't work. I get the following for all requests:

System.Exception : {"Message":"Authorization has been denied for this request."}

How do I Authenticate with the In-Memory OWIN test server or at least by-pass the authentication?

like image 613
mutex Avatar asked Nov 11 '13 01:11

mutex


People also ask

What's the difference between unit test and integration test?

While unit tests always take results from a single unit, such as a function call, integration tests may aggregate results from various parts and sources. In an integration test, there is no need to mock away parts of the application. You can replace external systems, but the application works in an integrated way.

Which package component can be added for integration testing of the .NET core API applications?

Mvc. Testing package is used to configure the test host and test server, the TestHost and TestServer packages don't require direct package references in the test app's project file or developer configuration in the test app.


1 Answers

I've been able to workaround this in a way that I'm sure is sub-optimal, but will have to do until I come across a better solution or one of you fine folk tells me a better way to do this :) I've done it as follows:

  1. In my Startup class I've added a CreateAuthFilter hook which we'll see later is used only in integration tests:

    // Sample Startup class
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var config = new HttpConfiguration();
    
            // Use CreateFilter Method to create Authorisation Filter -  if not null add it
            var authFilter = CreateAuthFilter();
            if(authFilter != null)
                config.Filters.Add(authFilter);
    
            // Other configuration and middleware...
        }
    
        public static Func<IFilter> CreateAuthFilter = () => null;
    }
    
  2. Implemented an Authorization Filter which will only be used in Integration Tests:

    public class TestAuthFilter : IAuthenticationFilter
    {
        static TestAuthFilter()
        {
            TestUserId = "TestDomain\\TestUser";
        }
    
        public bool AllowMultiple { get; private set; }
    
        public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
        {
            context.Principal = new ClientRolePrincipal(new HttpListenerBasicIdentity(TestUserId, "password")); ;
        }
    
        public static string TestUserId { get; set; }
    
        public async Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
        {
    
        }
    }
    
  3. In the SetUp code for my Integration Tests I inject the Test Authorization Filter:

    Startup.CreateAuthFilter = () => new TestAuthFilter();
    var TestServer = TestServer.Create<Startup>();
    
  4. When needed in specific tests, I set the TestUserId to a known value, and other tests just seem to work because the Auth Filter is present:

    TestAuthFilter.TestUserId = testUser.UserId;
    

I'm sharing this here incase it helps others out there, but please someone tell me a better way! At the very least I'm sure there's a better way to inject my Test Filter without including code in Startup... I just haven't thought of it.

like image 197
mutex Avatar answered Sep 28 '22 17:09

mutex