Authentication with Microsoft.Owin.Testing.TestSer

2019-03-12 20:31发布

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?

1条回答
时光不老,我们不散
2楼-- · 2019-03-12 21:07

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.

查看更多
登录 后发表回答