I'm working on an ASP.NET Core 8 web app. I have a service for providing keys for signing JSON web tokens (to be used for authentication). The service is a dependency of JWT authentication.
I wonder how to provide the service in the authentication setup? I can't DI it in the ConfigureServices
method, because trying to do so would cause an error.
Here's the code (its relevant parts):
public class Startup()
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IKeyProvider, KeyProvider>();
services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
// ...
// here below I need DI to provide a KeyProvider instance:
IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
//...
});
}
public void Configure()
{
// ...
}
}
Any idea on how to solve this?
ADDED 2025-01-26 ->
More code on a typical JWT auth setup:
services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
o.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "SomeIssuer",
ValidAudience = "SomeAudience",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("ABCDEABCDEABCDEABCDEABCDEABCDEABCDE")),
ClockSkew = new TimeSpan(0)
};
});
While AddJwtBearer
allows you to configure JwtBearerOptions
, there is currently no overload that allow for the use DI out of the box.
Within the default AuthenticationBuilder
class there is a helper method used to add authentication schemes along with its strongly typed options configuration which is supposed to be derived from AuthenticationSchemeOptions
. See source code here if interested.
Taking advantage of the functionality available in the Options pattern in ASP.NET Core and referencing "Use DI services to configure options", the following example shows how services can be accessed from dependency injection while configuring options.
In this case JwtBearerOptions
public void ConfigureServices(IServiceCollection services) {
//make sure service registered
services.AddSingleton<IKeyProvider, KeyProvider>();
//adding authentication
var authenticationScheme = JwtBearerDefaults.AuthenticationScheme;
services.AddAuthentication(o => {
o.DefaultAuthenticateScheme = authenticationScheme;
o.DefaultChallengeScheme = authenticationScheme;
})
.AddJwtBearer(); //<-- Don't forget to include this so `JwtBearerOptions` is added to configuration
//now configuring options
services
.AddOptions<JwtBearerOptions>(authenticationScheme) //Doing this to get access to OptionsBuilder<TOptions>
.PostConfigure<IKeyProvider>((options, keyProvider) => { //Use DI services to configure options
//Using OptionsBuilder<TOptions>.PostConfigure
//Note: These are run after all Configure(Action<TOptions>).
options.TokenValidationParameters = new TokenValidationParameters {
//...omitted for brevity
IssuerSigningKeyResolver = (string token, SecurityToken securityToken,string identifier, TokenValidationParameters parameters) => {
List<SecurityKey> keys = new List<SecurityKey>();
//...
//use KeyProvider instance to get keys
//keys.Add(signingKey);
//...
return keys;
},
//...omitted for brevity
};
});
}
Note that OptionsBuilder<TOptions>
provides overloads of Configure
and PostConfigure
that allow the use of five services to configure options.
This will allow for more services if needed to satisfy the desired functionality.