Update JwtBearerOptions via DI after configuration when secret or issuer changes
See original GitHub issueSimilar to Better way to get the Json Web Key in AddJwtBearer and Use object in DI when AddJwtBearer, I wanted a way to have my Jwt secret/issuer come from a configuration source that could be updated and have our sites/apis resilient enough to automatically use them if they changed without requiring a restart of each site that depended on the credentials.
I wanted to follow up with this question on either of those tasks but both were closed. I don’t know if @HaoK wants to weigh in a bit since his ‘tip’ is what got Voccano’s code working in issue #21491, but the issue is, for some reason DI for TheKeep (my object) was successfully injected, but the exposed properties were not updated at the time of my UpdatableJwtBearerOptions class. When it was ultimately injected into the minimal api endpoint call, they WERE updated there.
The relevant code is below, and I’ll post screen shots after that represent:
- Start api with issuer set to ‘bad.*’
- Make a request with ‘bad.*’ and receive 401 unauthorized (correct).
- Update configuration source to remove ‘bad.’
- Make a request and receive 200 (correct)
- First screen will be debug inside
UpdatableJwtBearerOptionsand second will be inside minimal api endpoint.
Anything obvious as to why TheKeep wouldn’t work?
Configure JwtBearerOptions and ‘TheKeep’ (my configuration class)
builder.Services
.Configure<TheKeepSettings>( builder.Configuration.GetSection( "TheKeep" ) ) // configuration in here
.AddScoped<Domain.Configuration.TheKeep>()
builder.Services.AddTransient<IConfigureOptions<JwtBearerOptions>, UpdatableJwtBearerOptions>();
TheKeep snippet showing DI of ‘IConfiguration’ and Jwt property
public TheKeep( IOptionsSnapshot<TheKeepSettings> theKeepSettings )
{
this.Jwt = theKeepSettings.Value.Jwt;
}
public Jwt Jwt { get; init; }
UpdatableJwtBearerOptions Class implementation to set Jwt secret/issuer
public class UpdatableJwtBearerOptions : IConfigureNamedOptions<JwtBearerOptions>
{
private readonly IConfiguration configuration;
private readonly Domain.Configuration.TheKeep theKeep;
public UpdatableJwtBearerOptions( IConfiguration configuration, Domain.Configuration.TheKeep theKeep )
{
this.configuration = configuration;
this.theKeep = theKeep;
}
public void Configure( string? name, JwtBearerOptions options )
{
options.TokenValidationParameters.IssuerSigningKeyResolver = ( token, securityToken, kid, validationParameters ) =>
{
validationParameters.ValidIssuer = configuration.GetValue<string>( "TheKeep:Jwt:WebServiceProxy:Issuer" );
var issuerFromTheKeep = theKeep.Jwt.WebServiceProxy.Issuer;
return new List<SecurityKey>()
{
new SymmetricSecurityKey( Encoding.ASCII.GetBytes( configuration.GetValue<string>( "TheKeep:Jwt:WebServiceProxy:Secret" )! ) )
};
};
}
public void Configure( JwtBearerOptions options ) => throw new NotImplementedException();
}
Snippet of minimal api…
public override async Task HandleAsync( NexgenRequest r, CancellationToken c )
{
var issuerFromTheKeep = theKeep.Jwt.WebServiceProxy.Issuer;
// ... code omitted
}
TheKeep not updated yet…
TheKeep updated…
Issue Analytics
- State:
- Created 2 months ago
- Comments:13 (9 by maintainers)
Top Related StackOverflow Question
If you want to continue using TheKeep, I would probably just make it a Singleton and change the property to query the IOptionsMonitor in the getter.
@halter73 Thanks for the ideas. So I didn’t provide enough code I don’t think 😃 But maybe you already knew what I was doing.
Here is my builder registration
And from above, my constructor for
UpdatableJwtBearerOptionsis:As is, you were right.
Configureone time, but hit theIssuerSigningKeyResolverdelegate every request.configuration.GetValue<string>()was updated, but theKeep value wasn’t.UpdatableJwtBearerOptionsand as you probably know, it was only hit once, which surprised me since it was registered as Transient. So obviously, my reference totheKeepwas never updated.I put in your
builder.Services.AddSingleton<IOptionsChangeTokenSource<JwtBearerOptions>>suggestion and:IssuerSigningKeyResolverdelegate.theKeepreference.So currently, I simply work with an
IConfigurationobject insideUpdatableJwtBearerOptions, don’t need to resolve a scopedTheKeepto pass in, and not using your ‘TokenSource’ suggestion. The only downside to this is accessing the configuration viaGetValue<string>( path )instead of strongly typed properties ofTheKeep.If you see anything that might allow me to use
TheKeep, let me know.