refactor(security): JwtBearer validation via IPostConfigureOptions (F2)

Eliminates the services.BuildServiceProvider() captive-provider antipattern
(ASP0000) inside AddJwtBearer. The new ConfigureJwtBearerFromTokenService
resolves JwtTokenService from the real DI container at runtime and stays
in lock-step with JwtTokenService.BuildValidationParameters.

All 27 Security.Tests stay green, including the F1 integration tests that
exercise /auth/token through the real bearer pipeline.
This commit is contained in:
Joseph Doherty
2026-05-26 06:18:00 -04:00
parent b266f63cd7
commit 45a8c79ffe

View File

@@ -11,6 +11,23 @@ using ZB.MOM.WW.OtOpcUa.Security.Ldap;
namespace ZB.MOM.WW.OtOpcUa.Security; namespace ZB.MOM.WW.OtOpcUa.Security;
/// <summary>
/// Resolves <see cref="JwtTokenService"/> from the real DI container at runtime so the bearer
/// pipeline's <see cref="Microsoft.IdentityModel.Tokens.TokenValidationParameters"/> stay in
/// lock-step with <see cref="JwtTokenService.BuildValidationParameters"/>. Replaces the prior
/// <c>services.BuildServiceProvider()</c> antipattern (ASP0000) that built a captive provider
/// from inside <c>.AddJwtBearer</c>.
/// </summary>
internal sealed class ConfigureJwtBearerFromTokenService(JwtTokenService tokenService)
: IPostConfigureOptions<JwtBearerOptions>
{
public void PostConfigure(string? name, JwtBearerOptions options)
{
if (name != JwtBearerDefaults.AuthenticationScheme) return;
options.TokenValidationParameters = tokenService.BuildValidationParameters();
}
}
public static class ServiceCollectionExtensions public static class ServiceCollectionExtensions
{ {
/// <summary> /// <summary>
@@ -52,12 +69,9 @@ public static class ServiceCollectionExtensions
return Task.CompletedTask; return Task.CompletedTask;
}; };
}) })
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, o => .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, _ => { /* parameters set by IPostConfigureOptions below */ });
{
using var scope = services.BuildServiceProvider().CreateScope(); services.AddSingleton<IPostConfigureOptions<JwtBearerOptions>, ConfigureJwtBearerFromTokenService>();
var jwt = scope.ServiceProvider.GetRequiredService<JwtTokenService>();
o.TokenValidationParameters = jwt.BuildValidationParameters();
});
services.AddAuthorization(o => services.AddAuthorization(o =>
{ {