Search code examples
c#dependency-injectionintegration-testingasp.net-core-2.0identityserver4

Using dependency injection to replace JWT Bearer Options in ASP.NET Core


In the web API, I'm securing with Jwt Auth, I have the following ConfigureServices Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(this.Configuration.GetConnectionString("DefaultConnection")));
    // Some additional application dependencies here with AddTransient()...
    services.AddMvc();
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
           options.Authority = "xxxxx";
           options.RequireHttpsMetadata = false;
           options.Audience = "xxxxx";
           options.BackchannelHttpHandler = BackChannelHandler;
        });
}

Here, BackChannelHandler is a public property of the Startup.cs:

public static HttpMessageHandler BackChannelHandler { get; set; }

The reason why I'm using this property is that when I'm doing integration testing using xUnit, I'm using in-memory TestServer from Microsoft.AspNetCore.TestHost. So, I need to register backchannel handler in TestFixture class like

testIdentityServer = new TestServer(identityServerBuilder);
My.Project.Startup.BackChannelHandler = testIdentityServer.CreateHandler();
testApiServer = new TestServer(apiServerBuilder);

While this is working fine, I would like to override either the services.AddAuthentication() or AddJwtBearer() in the DI container, so I can inject the authentication middleware configuration for testing purposes instead of using a public static property just like how I would do with any other dependency. When I try to do this using:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
       options.Authority = "xxxxx";
       options.RequireHttpsMetadata = false;
       options.Audience = "xxxxx";
       options.BackchannelHttpHandler = testIdentityServer.CreateHandler();
    });

in TestFixture.cs I get the error: authentication scheme with "Bearer token" already exists.

How can I do this with correctly ASP.NET Core?


Solution

  • The behaviour you encounter is normal as you can't have 2 handlers for the same authentication scheme. Here, AddJwtBearer adds a handler for the authentication scheme Bearer, which value comes from JwtBearerDefaults.AuthenticationScheme.

    The lambda expression you pass to the AddJwtBearer is registered as a named option configuration, the name it's registered against being the authentication scheme.

    So here's what you could do in your test project:

    services.PostConfigure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        options.BackchannelHttpHandler = testIdentityServer.CreateHandler();
    });
    

    Doing this will not change the authentication schemes that are already registered in the container but will only modify the options associated with the JWT handler.