From 9d6a4b7b32f6f79f4b1d791cccfe69c87fe5c17e Mon Sep 17 00:00:00 2001 From: Luiz Adolfo Date: Tue, 16 Jun 2026 09:07:27 -0300 Subject: [PATCH] Fix IHttpContextAccessor registration and update handler implementation for consistent cache key building --- CHANGELOG.md | 7 +++++++ .../Extensions/HttpClientBuilderExtensions.cs | 7 +++++-- .../AuthorizationInterceptorHandler.cs | 11 ++++------- src/Directory.Build.props | 2 +- .../HttpClientBuilderExtensionsTests.cs | 18 ++++++++++++++++++ .../AuthorizationInterceptorHandlerTests.cs | 13 ++----------- 6 files changed, 37 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d55ef8..f785e1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. +## [6.0.1] - 2026-06-16 + +### Fixed + +- Resolved `IHttpContextAccessor` directly in the authorization handler instead of creating a runtime service scope for cache key building. +- Registered `IHttpContextAccessor` for all `AddAuthorizationInterceptorHandler` overloads so `CacheKeyBuilder` works consistently. + ## [6.0.0] - 2026-06-15 ### Added diff --git a/src/AuthorizationInterceptor/Extensions/HttpClientBuilderExtensions.cs b/src/AuthorizationInterceptor/Extensions/HttpClientBuilderExtensions.cs index f5524c6..5d487d2 100644 --- a/src/AuthorizationInterceptor/Extensions/HttpClientBuilderExtensions.cs +++ b/src/AuthorizationInterceptor/Extensions/HttpClientBuilderExtensions.cs @@ -3,6 +3,7 @@ using AuthorizationInterceptor.Handlers; using AuthorizationInterceptor.Options; using AuthorizationInterceptor.Strategies; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -32,7 +33,7 @@ public static IHttpClientBuilder AddAuthorizationInterceptorHandler(this IHtt CreateAuthenticationHandler(provider), CreateStrategy(provider, builder, optionsInstance.Interceptors), provider.GetRequiredService(), - provider.GetRequiredService(), + provider.GetRequiredService(), optionsInstance.CacheKeyBuilder )); @@ -50,6 +51,8 @@ public static IHttpClientBuilder AddAuthorizationInterceptorHandler(this IHttpCl { ArgumentNullException.ThrowIfNull(authHandlerImpl); + builder.Services.AddHttpContextAccessor(); + var optionsInstance = RequireOptions(options); builder.AddHttpMessageHandler(provider => new AuthorizationInterceptorHandler( builder.Name, @@ -57,7 +60,7 @@ public static IHttpClientBuilder AddAuthorizationInterceptorHandler(this IHttpCl authHandlerImpl.Invoke(provider), CreateStrategy(provider, builder, optionsInstance.Interceptors), provider.GetRequiredService(), - provider.GetRequiredService(), + provider.GetRequiredService(), optionsInstance.CacheKeyBuilder )); diff --git a/src/AuthorizationInterceptor/Handlers/AuthorizationInterceptorHandler.cs b/src/AuthorizationInterceptor/Handlers/AuthorizationInterceptorHandler.cs index 3dda56f..71de701 100644 --- a/src/AuthorizationInterceptor/Handlers/AuthorizationInterceptorHandler.cs +++ b/src/AuthorizationInterceptor/Handlers/AuthorizationInterceptorHandler.cs @@ -3,7 +3,6 @@ using AuthorizationInterceptor.Strategies; using AuthorizationInterceptor.Utils; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace AuthorizationInterceptor.Handlers; @@ -15,17 +14,17 @@ internal class AuthorizationInterceptorHandler : DelegatingHandler private readonly IAuthenticationHandler _authenticationHandler; private readonly IAuthorizationInterceptorStrategy _strategy; private readonly ILogger _logger; - private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly IHttpContextAccessor _httpContextAccessor; private readonly Func? _cacheKeyBuilder; - public AuthorizationInterceptorHandler(string name, Func unauthenticatedPredicate, IAuthenticationHandler authenticationHandler, IAuthorizationInterceptorStrategy strategy, ILoggerFactory loggerFactory, IServiceScopeFactory serviceScopeFactory, Func? cacheKeyBuilder = null) + public AuthorizationInterceptorHandler(string name, Func unauthenticatedPredicate, IAuthenticationHandler authenticationHandler, IAuthorizationInterceptorStrategy strategy, ILoggerFactory loggerFactory, IHttpContextAccessor httpContextAccessor, Func? cacheKeyBuilder = null) { _name = name; _strategy = strategy; _authenticationHandler = authenticationHandler; _unauthenticatedPredicate = unauthenticatedPredicate; _logger = loggerFactory.CreateLogger("AuthorizationInterceptorHandler"); - _serviceScopeFactory = serviceScopeFactory; + _httpContextAccessor = httpContextAccessor; _cacheKeyBuilder = cacheKeyBuilder; } @@ -74,9 +73,7 @@ private async Task SendWithInterceptorAsync(HttpRequestMess if (_cacheKeyBuilder is null) return null; - using var scope = _serviceScopeFactory.CreateScope(); - var httpContextAccessor = scope.ServiceProvider.GetRequiredService(); - return _cacheKeyBuilder.Invoke(httpContextAccessor); + return _cacheKeyBuilder.Invoke(_httpContextAccessor); } private HttpRequestMessage AddHeaders(HttpRequestMessage request, AuthorizationHeaders headers, string? cacheKeySuffix) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index c887c11..67e5888 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -3,7 +3,7 @@ latest enable enable - 6.0.0 + 6.0.1 net8.0;net9.0;net10.0 Authorization Interceptor Adolfok3 diff --git a/tests/AuthorizationInterceptor.Tests/Extensions/HttpClientBuilderExtensionsTests.cs b/tests/AuthorizationInterceptor.Tests/Extensions/HttpClientBuilderExtensionsTests.cs index ae7229c..b22195c 100644 --- a/tests/AuthorizationInterceptor.Tests/Extensions/HttpClientBuilderExtensionsTests.cs +++ b/tests/AuthorizationInterceptor.Tests/Extensions/HttpClientBuilderExtensionsTests.cs @@ -1,5 +1,6 @@ using AuthorizationInterceptor.Extensions; using AuthorizationInterceptor.Tests.Utils; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; namespace AuthorizationInterceptor.Tests.Extensions; @@ -93,4 +94,21 @@ public void AddAuthorizationInterceptorHandler_WithOptionsAndAuthHandler_ShouldB // Assert Assert.Null(Record.Exception(act)); } + + [Fact] + public void AddAuthorizationInterceptorHandler_WithAuthHandler_ShouldRegisterHttpContextAccessor() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddHttpClient("Test") + .AddAuthorizationInterceptorHandler((provider) => ActivatorUtilities.CreateInstance(provider), options => + { + options.CacheKeyBuilder = accessor => accessor.HttpContext?.Request.Headers["x-mycustom-header"].ToString(); + }); + + // Assert + Assert.Contains(services, descriptor => descriptor.ServiceType == typeof(IHttpContextAccessor)); + } } diff --git a/tests/AuthorizationInterceptor.Tests/Handlers/AuthorizationInterceptorHandlerTests.cs b/tests/AuthorizationInterceptor.Tests/Handlers/AuthorizationInterceptorHandlerTests.cs index 6f0d678..6297ac7 100644 --- a/tests/AuthorizationInterceptor.Tests/Handlers/AuthorizationInterceptorHandlerTests.cs +++ b/tests/AuthorizationInterceptor.Tests/Handlers/AuthorizationInterceptorHandlerTests.cs @@ -16,7 +16,6 @@ public class AuthorizationInterceptorHandlerTests private readonly IAuthorizationInterceptorStrategy _strategy; private readonly IAuthenticationHandler _authenticationHandler; private readonly HttpClient _client; - private readonly IServiceScopeFactory _serviceScopeFactory; private readonly Func? _cacheKeyBuilder; private readonly IHttpContextAccessor _httpContextAccessor; @@ -24,7 +23,6 @@ public AuthorizationInterceptorHandlerTests() { _logger = Substitute.For(); _logger.IsEnabled(LogLevel.Debug).Returns(true); - _serviceScopeFactory = Substitute.For(); _cacheKeyBuilder = Substitute.For?>(); var loggerFactory = Substitute.For(); @@ -39,19 +37,13 @@ public AuthorizationInterceptorHandlerTests() }); httpContext.Request.Returns(httpRequest); _httpContextAccessor.HttpContext.Returns(httpContext); - var serviceScope = Substitute.For(); - var services = new ServiceCollection(); - services.AddSingleton(_httpContextAccessor); - var provider = services.BuildServiceProvider(); - serviceScope.ServiceProvider.Returns(provider); - _serviceScopeFactory.CreateScope().Returns(serviceScope); _cacheKeyBuilder!.Invoke(_httpContextAccessor).Returns((_) => null); Func func = f => f.StatusCode == System.Net.HttpStatusCode.Unauthorized; _strategy = Substitute.For(); _authenticationHandler = Substitute.For(); - var handler = new AuthorizationInterceptorHandler("test", func, _authenticationHandler, _strategy, loggerFactory, _serviceScopeFactory, _cacheKeyBuilder); + var handler = new AuthorizationInterceptorHandler("test", func, _authenticationHandler, _strategy, loggerFactory, _httpContextAccessor, _cacheKeyBuilder); handler.InnerHandler = new MockAuthorizationInterceptorHandler(); _client = new HttpClient(handler); } @@ -97,7 +89,7 @@ public async Task SendAsync_WithoutCacheKeyBuilder_ShouldUseDefaultCacheKey() _authenticationHandler, _strategy, loggerFactory, - _serviceScopeFactory); + _httpContextAccessor); handler.InnerHandler = new MockAuthorizationInterceptorHandler(); using var client = new HttpClient(handler); @@ -108,7 +100,6 @@ public async Task SendAsync_WithoutCacheKeyBuilder_ShouldUseDefaultCacheKey() //Assert Assert.True(response.IsSuccessStatusCode); - _serviceScopeFactory.DidNotReceive().CreateScope(); await _strategy.Received(1).GetHeadersAsync("test", _authenticationHandler, null, Arg.Any()); }