13

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?

3 Answers 3

9

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.

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

Comments

0

Not a solution per se, but if you don't want to change your entire testing framework from Microsoft.AspNetCore.Mvc.Testing to something else, the following temporary workaround may suffice you. Simply turn off security as follows and run your test.

In Program.cs, comment these lines out:

app.UseAuthentication();
app.UseAuthorization();

Also turn off authorization in the controller you are testing, as by commenting out the Authorize attribute:

//[Authorize]
public class MyController : ControllerBase
{
    ...
}

Now you can run your tests. Just don't forget to reverse the changes when you're done.

Comments

0

That is not supported by design

WebApplicationFactory doesn't use kestrel, it uses the in-memory TestServer and in-memory HttpClient. Neither of those support the features needed for Negotiate auth.

I dropped Microsoft.AspNetCore.Mvc.Testing and used a custom NUnit base class instead:

public class AspNetCoreIntegrationTestBase
{
  public CancellationTokenSource? CancellationTokenSource { get; set; }

  public Task? TestWebserver { get; set; }

  [OneTimeSetUp]
  public void Setup()
  {
    CancellationTokenSource = new CancellationTokenSource();
    TestWebserver = Program.CreateHostBuilder(Array.Empty<string>()).Build().StartAsync(CancellationTokenSource.Token);
  }

  [OneTimeTearDown]
  public void CleanUp()
  {
    CancellationTokenSource?.Cancel();
    TestWebserver?.Dispose();
  }
}

The httpclient to use needs to be initialized with

UseDefaultCredentials = true

Comments

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.