diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpCorsPolicyService.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpCorsPolicyService.cs index 57158db720..c1932d0435 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpCorsPolicyService.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpCorsPolicyService.cs @@ -40,7 +40,7 @@ namespace Volo.Abp.IdentityServer AbsoluteExpirationRelativeToNow = Options.Caching.CorsExpiration }); - var isAllowed = cacheItem.AllowedOrigins.Contains(origin, StringComparer.OrdinalIgnoreCase); + var isAllowed = await IsOriginAllowedAsync(cacheItem.AllowedOrigins, origin); if (!isAllowed) { @@ -63,5 +63,10 @@ namespace Volo.Abp.IdentityServer }; } } + + protected virtual Task IsOriginAllowedAsync(string[] allowedOrigins, string origin) + { + return Task.FromResult(allowedOrigins.Contains(origin, StringComparer.OrdinalIgnoreCase)); + } } } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerServiceCollectionExtensions.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerServiceCollectionExtensions.cs index 4bb5b2ca6a..baa04a6e5f 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerServiceCollectionExtensions.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ -using IdentityServer4.Validation; +using IdentityServer4.Services; +using IdentityServer4.Validation; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -15,5 +16,10 @@ namespace Volo.Abp.IdentityServer { services.Replace(ServiceDescriptor.Transient()); } + + public static void AddAbpWildcardSubdomainCorsPolicyService(this IServiceCollection services) + { + services.Replace(ServiceDescriptor.Transient()); + } } } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService.cs new file mode 100644 index 0000000000..1a9d146ca9 --- /dev/null +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading.Tasks; +using IdentityServer4.Configuration; +using Microsoft.Extensions.Options; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Text.Formatting; + +namespace Volo.Abp.IdentityServer +{ + public class AbpWildcardSubdomainCorsPolicyService : AbpCorsPolicyService + { + public AbpWildcardSubdomainCorsPolicyService( + IDistributedCache cache, + IHybridServiceScopeFactory hybridServiceScopeFactory, + IOptions options) + : base(cache, hybridServiceScopeFactory, options) + { + + } + + protected override async Task IsOriginAllowedAsync(string[] allowedOrigins, string origin) + { + var isAllowed = await base.IsOriginAllowedAsync(allowedOrigins, origin); + if (isAllowed) + { + return true; + } + + foreach (var url in allowedOrigins) + { + var extractResult = FormattedStringValueExtracter.Extract(origin, url, ignoreCase: true); + if (extractResult.IsMatch) + { + return true; + } + + if (url.Replace("{0}.", "").Contains(origin, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + } +} diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService_Tests.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService_Tests.cs new file mode 100644 index 0000000000..fcb8e756e0 --- /dev/null +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService_Tests.cs @@ -0,0 +1,40 @@ +using System.Threading.Tasks; +using IdentityServer4.Services; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Volo.Abp.IdentityServer +{ + public class AbpWildcardSubdomainCorsPolicyService_Tests : AbpIdentityServerTestBase + { + private readonly ICorsPolicyService _corsPolicyService; + + public AbpWildcardSubdomainCorsPolicyService_Tests() + { + _corsPolicyService = GetRequiredService(); + } + + protected override void AfterAddApplication(IServiceCollection services) + { + services.AddAbpWildcardSubdomainCorsPolicyService(); + } + + [Fact] + public void Should_Register_AbpWildcardSubdomainCorsPolicyService() + { + _corsPolicyService.GetType().ShouldBe(typeof(AbpWildcardSubdomainCorsPolicyService)); + } + + [Fact] + public async Task IsOriginAllowedAsync() + { + (await _corsPolicyService.IsOriginAllowedAsync("https://client1-origin.com")).ShouldBeTrue(); + (await _corsPolicyService.IsOriginAllowedAsync("https://client2-origin.com")).ShouldBeFalse(); + + (await _corsPolicyService.IsOriginAllowedAsync("https://abp.io")).ShouldBeTrue(); + (await _corsPolicyService.IsOriginAllowedAsync("https://t1.abp.io")).ShouldBeTrue(); + (await _corsPolicyService.IsOriginAllowedAsync("https://t1.ng.abp.io")).ShouldBeTrue(); + } + } +} diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs index a2d9490404..e2bd202ec1 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs @@ -148,6 +148,7 @@ namespace Volo.Abp.IdentityServer }; client.AddCorsOrigin("https://client1-origin.com"); + client.AddCorsOrigin("https://{0}.abp.io"); client.AddClaim(nameof(ClientClaim.Value), nameof(ClientClaim.Type)); client.AddGrantType(nameof(ClientGrantType.GrantType)); client.AddIdentityProviderRestriction(nameof(ClientIdPRestriction.Provider));