From 45a8c79ffe429d93035bc5129664001a1eb328ef Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 26 May 2026 06:18:00 -0400 Subject: [PATCH] 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. --- .../ServiceCollectionExtensions.cs | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Security/ServiceCollectionExtensions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Security/ServiceCollectionExtensions.cs index 657c794..4dbc1e9 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Security/ServiceCollectionExtensions.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Security/ServiceCollectionExtensions.cs @@ -11,6 +11,23 @@ using ZB.MOM.WW.OtOpcUa.Security.Ldap; namespace ZB.MOM.WW.OtOpcUa.Security; +/// +/// Resolves from the real DI container at runtime so the bearer +/// pipeline's stay in +/// lock-step with . Replaces the prior +/// services.BuildServiceProvider() antipattern (ASP0000) that built a captive provider +/// from inside .AddJwtBearer. +/// +internal sealed class ConfigureJwtBearerFromTokenService(JwtTokenService tokenService) + : IPostConfigureOptions +{ + public void PostConfigure(string? name, JwtBearerOptions options) + { + if (name != JwtBearerDefaults.AuthenticationScheme) return; + options.TokenValidationParameters = tokenService.BuildValidationParameters(); + } +} + public static class ServiceCollectionExtensions { /// @@ -52,12 +69,9 @@ public static class ServiceCollectionExtensions return Task.CompletedTask; }; }) - .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, o => - { - using var scope = services.BuildServiceProvider().CreateScope(); - var jwt = scope.ServiceProvider.GetRequiredService(); - o.TokenValidationParameters = jwt.BuildValidationParameters(); - }); + .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, _ => { /* parameters set by IPostConfigureOptions below */ }); + + services.AddSingleton, ConfigureJwtBearerFromTokenService>(); services.AddAuthorization(o => {