Search code examples
c#asp.net-mvcasp.net-coreintegration-testing

Negotiate authentication and Microsoft.AspNetCore.Mvc.Testing


learn.microsoft.com recommends to use Microsoft.AspNetCore.Mvc.Testing for integration tests and it seems to be a great choice, but it seems that they missed to add the ability to test with NegotiateDefaults.AuthenticationScheme.

Adding to Startup.cs/ConfigureServices

 services.AddAuthentication (NegotiateDefaults.AuthenticationScheme).AddNegotiate ();

causes the tests to fail with

Message: System.NotSupportedException : Negotiate authentication requires a server that supports IConnectionItemsFeature like Kestrel.

Does anybody know how to use Microsoft.AspNetCore.Mvc.Testing with endpoints that use NegotiateDefaults.AuthenticationScheme? Is it just not supported, like the exception claims?


Solution

  • There is a much better way to do this my dudes :) and its supported way. check this out, we can implement IServerIntegratedAuth

    public class TestServerIntegratedAuth : IServerIntegratedAuth
    {
        public bool IsEnabled => true;
    
        public string AuthenticationScheme => "TestScheme";
    }
    

    this will instruct NegotiateHandles to forward authentication to TestScheme, then in tests you can add this IServerIntegratedAuth implementation to a container and register TestScheme if you want to perform authentication

    var client = factory.WithWebHostBuilder(b =>
        {
            b.ConfigureServices(services =>
            {
                services.AddSingleton<IServerIntegratedAuth, TestServerIntegratedAuth>();
    
                services
                    .AddAuthentication()
                    .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>("TestScheme", options => { });
            });
    
        }).CreateDefaultClient();
    

    this TestAuthHandler is from ms docs

    public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
    {
        public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
            ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
            : base(options, logger, encoder, clock)
        {
        }
    
        protected override Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
            var identity = new ClaimsIdentity(claims, "Test");
            var principal = new ClaimsPrincipal(identity);
            var ticket = new AuthenticationTicket(principal, "TestScheme");
    
            var result = AuthenticateResult.Success(ticket);
    
            return Task.FromResult(result);
        }
    }
    

    and that's it. Works like a charm.

    The same thing IIS integration implements in order to perform negotiation natively.