Description: I'm setting up integration tests for an ASP.NET Core application using the minimal hosting model in .NET 6. I am trying to mock the authentication process using a custom authentication handler. Despite my setup, the requests are returning a 401 Unauthorized status. Here are the relevant parts of my code:
TestWebApplicationFactory
namespace TX.BehaviorSpecificTests
{
public class TestWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Remove the existing DbContext registration.
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<AppDbContext>));
if (descriptor != null)
{
services.Remove(descriptor);
}
// Add in-memory DbContext for testing.
services.AddDbContext<AppDbContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
});
// Add mock authentication.
services.AddAuthentication("Test")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>("Test", options => { });
// Add controllers from the main application.
services.AddControllers()
.AddApplicationPart(typeof(Program).Assembly);
});
builder.UseEnvironment("Development");
builder.Configure(app =>
{
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
});
}
}
And here is my TestAuthHandler used to Mock Authentication
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder)
: base(options, logger, encoder)
{
HandleAuthenticateAsync();
}
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 this is the test code:
[Binding]
public sealed class CalculatorStepDefinitions : IClassFixture<TestWebApplicationFactory<Program>>
{
private readonly TestWebApplicationFactory<Program> _factory;
private HttpClient _client;
private HttpResponseMessage _response;
public CalculatorStepDefinitions(TestWebApplicationFactory<Program> factory)
{
_factory = factory;
_client = _factory
.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false,
});
_client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(scheme: "TestScheme");
}
[Given("the second number is (.*)")]
public async Task GivenTheSecondNumberIs(int number)
{
try
{
_response = await _client.GetAsync("/api/WeatherForecast/GetToday");
_response.EnsureSuccessStatusCode();
var content = await _response.Content.ReadAsStringAsync();
Assert.True(_response.IsSuccessStatusCode);
}
catch (Exception ex)
{
throw;
}
}
This is the controller I am trying to call
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[AllowAnonymous]
[ApiController]
[Route("api/WeatherForecast")]
public class WeatherForecastController : ControllerBase
{
[HttpGet("GetToday")]
public IActionResult Get()
{
return Ok(new { Weather = "Sunny" });
}
}