diff --git a/framework/src/Volo.Abp.Http.Client.IdentityModel.Web/Volo/Abp/Http/Client/IdentityModel/Web/HttpContextAbpAccessTokenProvider.cs b/framework/src/Volo.Abp.Http.Client.IdentityModel.Web/Volo/Abp/Http/Client/IdentityModel/Web/HttpContextAbpAccessTokenProvider.cs index b993b006e3..a359a3ef10 100644 --- a/framework/src/Volo.Abp.Http.Client.IdentityModel.Web/Volo/Abp/Http/Client/IdentityModel/Web/HttpContextAbpAccessTokenProvider.cs +++ b/framework/src/Volo.Abp.Http.Client.IdentityModel.Web/Volo/Abp/Http/Client/IdentityModel/Web/HttpContextAbpAccessTokenProvider.cs @@ -1,10 +1,8 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Client.Authentication; -using Volo.Abp.Users; namespace Volo.Abp.Http.Client.IdentityModel.Web; @@ -26,7 +24,7 @@ public class HttpContextAbpAccessTokenProvider : IAbpAccessTokenProvider, ITrans return null; } - if (!httpContext.RequestServices.GetRequiredService().IsAuthenticated) + if (httpContext.User.Identity?.IsAuthenticated != true) { return null; } diff --git a/framework/test/Volo.Abp.Http.Client.IdentityModel.Web.Tests/Volo/Abp/Http/Client/IdentityModel/Web/HttpContextAbpAccessTokenProvider_Tests.cs b/framework/test/Volo.Abp.Http.Client.IdentityModel.Web.Tests/Volo/Abp/Http/Client/IdentityModel/Web/HttpContextAbpAccessTokenProvider_Tests.cs new file mode 100644 index 0000000000..f2a79016aa --- /dev/null +++ b/framework/test/Volo.Abp.Http.Client.IdentityModel.Web.Tests/Volo/Abp/Http/Client/IdentityModel/Web/HttpContextAbpAccessTokenProvider_Tests.cs @@ -0,0 +1,74 @@ +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.Http.Client.Authentication; +using Volo.Abp.Http.Client.IdentityModel.Web.Tests; +using Volo.Abp.Security.Claims; +using Volo.Abp.Testing; +using Xunit; + +namespace Volo.Abp.Http.Client.IdentityModel.Web; + +public class HttpContextAbpAccessTokenProvider_Tests : AbpIntegratedTest +{ + private readonly IAbpAccessTokenProvider _accessTokenProvider; + private readonly IHttpContextAccessor _httpContextAccessor; + + public HttpContextAbpAccessTokenProvider_Tests() + { + _accessTokenProvider = GetRequiredService(); + _httpContextAccessor = GetRequiredService(); + } + + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.Services.AddHttpContextAccessor(); + } + + [Fact] + public async Task Should_Forward_Token_For_Authenticated_User() + { + var identity = new ClaimsIdentity(TestTokenAuthHandler.SchemeName); + identity.AddClaim(new Claim(AbpClaimTypes.UserId, "d3c1a6b0-1234-4a6f-9e3b-8c2f1d4a5b6e")); + _httpContextAccessor.HttpContext = CreateHttpContext(new ClaimsPrincipal(identity)); + + (await _accessTokenProvider.GetTokenAsync()).ShouldBe(TestTokenAuthHandler.TestAccessToken); + } + + [Fact] + public async Task Should_Forward_Token_For_Authenticated_Client_Without_User() + { + // client_credentials token: authenticated identity but no user id claim. + var identity = new ClaimsIdentity(TestTokenAuthHandler.SchemeName); + identity.AddClaim(new Claim(AbpClaimTypes.ClientId, "test-client")); + _httpContextAccessor.HttpContext = CreateHttpContext(new ClaimsPrincipal(identity)); + + (await _accessTokenProvider.GetTokenAsync()).ShouldBe(TestTokenAuthHandler.TestAccessToken); + } + + [Fact] + public async Task Should_Not_Forward_Token_For_Anonymous_Request() + { + // No authentication type => Identity.IsAuthenticated is false (e.g. before authentication middleware). + _httpContextAccessor.HttpContext = CreateHttpContext(new ClaimsPrincipal(new ClaimsIdentity())); + + (await _accessTokenProvider.GetTokenAsync()).ShouldBeNull(); + } + + private static DefaultHttpContext CreateHttpContext(ClaimsPrincipal user) + { + var services = new ServiceCollection(); + services.AddLogging(); + services.AddAuthentication(TestTokenAuthHandler.SchemeName) + .AddScheme(TestTokenAuthHandler.SchemeName, _ => { }); + + return new DefaultHttpContext + { + RequestServices = services.BuildServiceProvider(), + User = user + }; + } +} diff --git a/framework/test/Volo.Abp.Http.Client.IdentityModel.Web.Tests/Volo/Abp/Http/Client/IdentityModel/Web/TestTokenAuthHandler.cs b/framework/test/Volo.Abp.Http.Client.IdentityModel.Web.Tests/Volo/Abp/Http/Client/IdentityModel/Web/TestTokenAuthHandler.cs new file mode 100644 index 0000000000..2bc41d239d --- /dev/null +++ b/framework/test/Volo.Abp.Http.Client.IdentityModel.Web.Tests/Volo/Abp/Http/Client/IdentityModel/Web/TestTokenAuthHandler.cs @@ -0,0 +1,32 @@ +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Volo.Abp.Http.Client.IdentityModel.Web; + +public class TestTokenAuthHandler : AuthenticationHandler +{ + public const string SchemeName = "TestToken"; + public const string TestAccessToken = "test-access-token"; + + public TestTokenAuthHandler( + IOptionsMonitor options, + ILoggerFactory logger, + UrlEncoder encoder) + : base(options, logger, encoder) + { + } + + protected override Task HandleAuthenticateAsync() + { + // The current HttpContext.User already carries the principal set up by the test. + // This handler only makes an "access_token" retrievable via HttpContext.GetTokenAsync. + var properties = new AuthenticationProperties(); + properties.StoreTokens(new[] { new AuthenticationToken { Name = "access_token", Value = TestAccessToken } }); + + var ticket = new AuthenticationTicket(Context.User, properties, SchemeName); + return Task.FromResult(AuthenticateResult.Success(ticket)); + } +}