I'm writing an integration test for my web service with JWT authentication. I'd like to test it with a token received from the real service. The problem is real tokens expire in 1 hour.
A possible way is to set options.TokenValidationParameters.ValidateLifetime
inside AddJwtBearer
of class Startup
below.
However, Startup
class is a also a code to be tested, so I don't want to change or replace it for testing.
Is there a neat way to test all JWT validation logic except expiration?
My project code:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddAuthentication("JWT")
.AddJwtBearer("JWT", options =>
{
options.Authority = "https://my-real-identity-server.com/";
options.Audience = "...";
// I don't want to disable lifetime validation in real life
// options.TokenValidationParameters.ValidateLifetime = false;
});
// Other stuff
}
public void Configure(IApplicationBuilder app) => app
.UseRouting()
.UseAuthentication()
.UseAuthorization()
.UseEndpoints(endpoints => endpoints.MapControllers());
}
public class TestController : ControllerBase
{
[Authorize]
[HttpGet("/validate")]
public string Get() => "success";
}
My test code:
public class HostBuilderTests
{
private IHost testHost;
private CancellationTokenSource cancel;
private HttpClient client;
[SetUp]
public async Task ShouldReturnStatus()
{
testHost = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
webBuilder
.UseStartup<Startup>()
.UseTestServer())
.ConfigureServices(services => services
.AddLogging(b => b.ClearProviders().AddNUnit()))
.Build();
cancel = new CancellationTokenSource(10000);
var server = testHost.Services.GetRequiredService<IServer>()
.ShouldBeOfType<TestServer>();
await testHost.StartAsync(cancel.Token);
client = server.CreateClient();
}
[TearDown]
public async Task TearDown()
{
await testHost.StopAsync(cancel.Token);
client.Dispose();
testHost.Dispose();
cancel.Dispose();
}
[Test]
[TestCase("<<JWT token copied from the real service>>")]
public async Task StatusShouldBeOk(string realToken)
{
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", realToken);
using var response = await client.GetAsync("/validate", cancel.Token);
response.StatusCode.ShouldBe(HttpStatusCode.OK);
}
}
CodePudding user response:
Finally I found a simple way to manage options for authentication handlers:
An authentication scheme is a name which corresponds to:
- An authentication handler.
- Options for configuring that specific instance of the handler.
See MSDN.
So it suffices to post-configure JwtBearerOptions
specifying the authentication scheme name "JWT"
as options instance name in the test setup:
testHost = Host.CreateDefaultBuilder()
// other setup
.ConfigureServices(services => services
.PostConfigure<JwtBearerOptions>("JWT",
op => op.TokenValidationParameters.ValidateLifetime = false)
// other setup
).Build();
Also it is possible to pass null
instead of "JWT"
as it written in comments:
// Null name is used to configure all named options.