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 633719ae9b..679078adbc 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 @@ -6,11 +6,11 @@ namespace Volo.Abp.IdentityServer { public static class AbpIdentityServerServiceCollectionExtensions { - public static void AddAbpStrictRedirectUriValidator(this IServiceCollection services, string domainFormat) + public static void AddAbpStrictRedirectUriValidator(this IServiceCollection services, params string[] domainFormats) { services.Configure(options => { - options.DomainFormat = domainFormat; + options.DomainFormats.AddRange(domainFormats); }); services.Replace(ServiceDescriptor.Transient()); diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpRedirectUriValidatorOptions.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpRedirectUriValidatorOptions.cs index 082aeb4523..e9bf575bfe 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpRedirectUriValidatorOptions.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpRedirectUriValidatorOptions.cs @@ -1,7 +1,14 @@ -namespace Volo.Abp.IdentityServer +using System.Collections.Generic; + +namespace Volo.Abp.IdentityServer { public class AbpRedirectUriValidatorOptions { - public string DomainFormat { get; set; } + public List DomainFormats { get; } + + public AbpRedirectUriValidatorOptions() + { + DomainFormats = new List(); + } } } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator.cs index 50baadaf2b..94f05b962b 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator.cs @@ -1,5 +1,5 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using IdentityServer4.Models; using IdentityServer4.Validation; @@ -8,11 +8,7 @@ using Volo.Abp.Text.Formatting; namespace Volo.Abp.IdentityServer { - /// - /// Default implementation of redirect URI validator. Validates the URIs against - /// the client's configured URIs. - /// - public class AbpStrictRedirectUriValidator : IRedirectUriValidator + public class AbpStrictRedirectUriValidator : StrictRedirectUriValidator { protected AbpRedirectUriValidatorOptions Options { get; } @@ -21,63 +17,38 @@ namespace Volo.Abp.IdentityServer Options = options.Value; } - /// - /// Checks if a given URI string is in a collection of strings (using ordinal ignore case comparison) - /// - /// The uris. - /// The requested URI. - /// - protected virtual bool StringCollectionContainsString(IEnumerable uris, string requestedUri) + public override async Task IsRedirectUriValidAsync(string requestedUri, Client client) + { + var isAllowed = await base.IsRedirectUriValidAsync(requestedUri, client); + return isAllowed || await IsRedirectUriValidWithDomainFormatsAsync(client.RedirectUris, requestedUri); + } + + public override async Task IsPostLogoutRedirectUriValidAsync(string requestedUri, Client client) + { + var isAllowed = await base.IsPostLogoutRedirectUriValidAsync(requestedUri, client); + return isAllowed || await IsRedirectUriValidWithDomainFormatsAsync(client.PostLogoutRedirectUris, requestedUri); + } + + protected virtual Task IsRedirectUriValidWithDomainFormatsAsync(IEnumerable uris, string requestedUri) { if (uris == null) { - return false; + return Task.FromResult(false); } foreach (var url in uris) { - if (url.Contains(requestedUri, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - if (url.Contains(Options.DomainFormat)) + if (Options.DomainFormats.Any(x => url.Contains(x))) { var extractResult = FormattedStringValueExtracter.Extract(requestedUri, url, ignoreCase: true); if (extractResult.IsMatch) { - return true; + return Task.FromResult(true); } } } - return false; - } - - /// - /// Determines whether a redirect URI is valid for a client. - /// - /// The requested URI. - /// The client. - /// - /// true is the URI is valid; false otherwise. - /// - public virtual Task IsRedirectUriValidAsync(string requestedUri, Client client) - { - return Task.FromResult(StringCollectionContainsString(client.RedirectUris, requestedUri)); - } - - /// - /// Determines whether a post logout URI is valid for a client. - /// - /// The requested URI. - /// The client. - /// - /// true is the URI is valid; false otherwise. - /// - public virtual Task IsPostLogoutRedirectUriValidAsync(string requestedUri, Client client) - { - return Task.FromResult(StringCollectionContainsString(client.PostLogoutRedirectUris, requestedUri)); + return Task.FromResult(false); } } } diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator_Tests.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator_Tests.cs new file mode 100644 index 0000000000..50f84e890e --- /dev/null +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator_Tests.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using IdentityServer4.Models; +using IdentityServer4.Validation; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Volo.Abp.IdentityServer +{ + public class AbpStrictRedirectUriValidator_Tests : AbpIdentityServerTestBase + { + private readonly IRedirectUriValidator _abpStrictRedirectUriValidator; + + private readonly Client _testClient = new Client + { + RedirectUris = new List + { + "https://{0}.api.abp.io:8080/signin-oidc", + "http://{0}.ng.abp.io/index.html" + }, + PostLogoutRedirectUris = new List + { + "https://{0}.api.abp.io:8080/signin-oidc", + "http://{0}.ng.abp.io/index.html" + } + }; + + public AbpStrictRedirectUriValidator_Tests() + { + _abpStrictRedirectUriValidator = GetRequiredService(); + } + + protected override void AfterAddApplication(IServiceCollection services) + { + services.AddAbpStrictRedirectUriValidator("{0}.api.abp.io:8080/signin-oidc", "{0}.ng.abp.io/index.html"); + } + + [Fact] + public void Should_Register_AbpStrictRedirectUriValidator() + { + _abpStrictRedirectUriValidator.GetType().ShouldBe(typeof(AbpStrictRedirectUriValidator)); + } + + [Fact] + public async Task IsRedirectUriValidAsync() + { + (await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("https://t1.api.abp.io:8080/signin-oidc", _testClient)).ShouldBeTrue(); + (await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("http://t2.ng.abp.io/index.html", _testClient)).ShouldBeTrue(); + + (await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("https://api.abp:8080/", _testClient)).ShouldBeFalse(); + (await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("http://ng.abp.io", _testClient)).ShouldBeFalse(); + (await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("https://api.t1.abp:8080/", _testClient)).ShouldBeFalse(); + (await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("http://ng.t1.abp.io", _testClient)).ShouldBeFalse(); + } + + [Fact] + public async Task IsPostLogoutRedirectUriValidAsync() + { + (await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("https://t1.api.abp.io:8080/signin-oidc", _testClient)).ShouldBeTrue(); + (await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("http://t2.ng.abp.io/index.html", _testClient)).ShouldBeTrue(); + + (await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("https://api.abp:8080/", _testClient)).ShouldBeFalse(); + (await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("http://ng.abp.io", _testClient)).ShouldBeFalse(); + (await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("https://api.t1.abp:8080/", _testClient)).ShouldBeFalse(); + (await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("http://ng.t1.abp.io", _testClient)).ShouldBeFalse(); + } + } +}