diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs b/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs index ba21ef11fd..4fc7c4ca75 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Authentication.OAuth; +using Volo.Abp.AspNetCore.Security; using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; using Volo.Abp.RemoteServices; @@ -16,5 +17,10 @@ public class AbpAspNetCoreAuthenticationOpenIdConnectModule : AbpModule public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.AddHttpClient(); + + Configure(options => + { + options.IgnoredScriptNoncePaths.Add("/signout-oidc"); + }); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs index 9bc15ae617..d0ba8b98c0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Linq; using System.Reflection; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Volo.Abp.Modularity; @@ -15,152 +13,36 @@ public static class ApplicationPartSorter { public static void Sort(ApplicationPartManager partManager, IModuleContainer moduleContainer) { - /* Performing a double Reverse() to preserve the original order for non-sorted parts - */ + var orderedModuleAssemblies = moduleContainer.Modules + .Select((moduleDescriptor, index) => new { moduleDescriptor.Assembly, index }) + .ToDictionary(x => x.Assembly, x => x.index); - var dependencyDictionary = CreateDependencyDictionary(partManager, moduleContainer); + var modulesAssemblies = moduleContainer.Modules.Select(x => x.Assembly).ToList(); + var sortedTypes = partManager.ApplicationParts + .Where(x => modulesAssemblies.Contains(GetApplicationPartAssembly(x))) + .OrderBy(x => orderedModuleAssemblies[GetApplicationPartAssembly(x)]) + .ToList(); - var sortedParts = partManager - .ApplicationParts - .Reverse() //First Revers - .SortByDependencies(p => dependencyDictionary[p]); + var sortIndex = 0; + var sortedParts = partManager.ApplicationParts + .Select(x => modulesAssemblies.Contains(GetApplicationPartAssembly(x)) ? sortedTypes[sortIndex++] : x) + .ToList(); - sortedParts.Reverse(); //Reverse again - - //Replace the original parts with the sorted parts partManager.ApplicationParts.Clear(); + sortedParts.Reverse(); foreach (var applicationPart in sortedParts) { partManager.ApplicationParts.Add(applicationPart); } } - private static Dictionary> CreateDependencyDictionary( - ApplicationPartManager partManager, IModuleContainer moduleContainer) - { - var dependencyDictionary = new Dictionary>(); - - foreach (var applicationPart in partManager.ApplicationParts) - { - dependencyDictionary[applicationPart] = - CreateDependencyList(applicationPart, partManager, moduleContainer); - } - - return dependencyDictionary; - } - - private static List CreateDependencyList( - ApplicationPart applicationPart, - ApplicationPartManager partManager, - IModuleContainer moduleContainer) + private static Assembly GetApplicationPartAssembly(ApplicationPart part) { - var list = new List(); - - if (applicationPart is AssemblyPart assemblyPart) + return part switch { - AddDependencies(list, assemblyPart, partManager, moduleContainer); - } - else if (applicationPart is CompiledRazorAssemblyPart compiledRazorAssemblyPart) - { - AddDependencies(list, compiledRazorAssemblyPart, partManager, moduleContainer); - } - - return list; - } - - private static void AddDependencies( - List list, - AssemblyPart assemblyPart, - ApplicationPartManager partManager, - IModuleContainer moduleContainer) - { - var dependedAssemblyParts = GetDependedAssemblyParts( - partManager, - moduleContainer, - assemblyPart - ); - - list.AddRange(dependedAssemblyParts); - - foreach (var dependedAssemblyPart in dependedAssemblyParts) - { - var viewsPart = GetViewsPartOrNull(partManager, dependedAssemblyPart); - if (viewsPart != null) - { - list.Add(viewsPart); - } - } - } - - private static void AddDependencies( - List list, - CompiledRazorAssemblyPart compiledRazorAssemblyPart, - ApplicationPartManager partManager, - IModuleContainer moduleContainer) - { - if (!compiledRazorAssemblyPart.Name.EndsWith(".Views")) - { - return; - } - - var originalAssemblyPart = GetOriginalAssemblyPartOrNull(compiledRazorAssemblyPart, partManager); - if (originalAssemblyPart == null) - { - return; - } - - list.Add(originalAssemblyPart); - } - - private static AssemblyPart[] GetDependedAssemblyParts( - ApplicationPartManager partManager, - IModuleContainer moduleContainer, - AssemblyPart assemblyPart) - { - var moduleDescriptor = GetModuleDescriptorForAssemblyOrNull(moduleContainer, assemblyPart.Assembly); - if (moduleDescriptor == null) - { - return Array.Empty(); - } - - var moduleDependedAssemblies = moduleDescriptor - .Dependencies - .SelectMany(d => d.AllAssemblies) - .ToArray(); - - return partManager.ApplicationParts - .OfType() - .Where(a => a.Assembly.IsIn(moduleDependedAssemblies)) - .Distinct() - .ToArray(); - } - - private static CompiledRazorAssemblyPart? GetViewsPartOrNull(ApplicationPartManager partManager, - ApplicationPart assemblyPart) - { - var viewsAssemblyName = assemblyPart.Name + ".Views"; - return partManager - .ApplicationParts - .OfType() - .FirstOrDefault(p => p.Name == viewsAssemblyName); - } - - private static AssemblyPart? GetOriginalAssemblyPartOrNull( - CompiledRazorAssemblyPart compiledRazorAssemblyPart, - ApplicationPartManager partManager) - { - var originalAssemblyName = compiledRazorAssemblyPart.Name.RemovePostFix(".Views"); - return partManager.ApplicationParts - .OfType() - .FirstOrDefault(p => p.Name == originalAssemblyName); - } - - private static IAbpModuleDescriptor? GetModuleDescriptorForAssemblyOrNull( - IModuleContainer moduleContainer, - Assembly assembly) - { - return moduleContainer - .Modules - .FirstOrDefault(m => m.AllAssemblies.Contains(assembly)); + AssemblyPart assemblyPart => assemblyPart.Assembly, + CompiledRazorAssemblyPart compiledRazorAssemblyPart => compiledRazorAssemblyPart.Assembly, + _ => throw new AbpException("Unknown application part type") + }; } } diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs index 2d231c46ee..f1f2c9016e 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs @@ -25,7 +25,9 @@ public class AbpSecurityHeadersMiddleware : AbpMiddlewareBase, ITransientDepende { var endpoint = context.GetEndpoint(); - if (endpoint?.Metadata.GetMetadata() != null) + if (endpoint?.Metadata.GetMetadata() != null || + await AlwaysIgnoreContentTypes(context) || + Options.Value.IgnoredScriptNoncePaths.Any(x => context.Request.Path.StartsWithSegments(x.EnsureStartsWith('/'), StringComparison.OrdinalIgnoreCase))) { await next.Invoke(context); return; @@ -41,13 +43,13 @@ public class AbpSecurityHeadersMiddleware : AbpMiddlewareBase, ITransientDepende AddHeader(context, "X-Frame-Options", "SAMEORIGIN"); var requestAcceptTypeHtml = context.Request.Headers["Accept"].Any(x => - x!.Contains("text/html") || x.Contains("*/*") || x.Contains("application/xhtml+xml")); + x!.Contains("text/html", StringComparison.OrdinalIgnoreCase) || + x.Contains("*/*", StringComparison.OrdinalIgnoreCase) || + x.Contains("application/xhtml+xml", StringComparison.OrdinalIgnoreCase)); if (!requestAcceptTypeHtml || !Options.Value.UseContentSecurityPolicyHeader - || await AlwaysIgnoreContentTypes(context) - || endpoint == null - || Options.Value.IgnoredScriptNoncePaths.Any(x => context.Request.Path.StartsWithSegments(x.EnsureStartsWith('/'), StringComparison.OrdinalIgnoreCase))) + || endpoint == null) { AddOtherHeaders(context); await next.Invoke(context); @@ -60,7 +62,6 @@ public class AbpSecurityHeadersMiddleware : AbpMiddlewareBase, ITransientDepende context.Items.Add(AbpAspNetCoreConsts.ScriptNonceKey, randomValue); } - context.Response.OnStarting(() => { if (context.Response.Headers.ContainsKey("Content-Security-Policy")) diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ApplicationPart/ApplicationPartSorter_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ApplicationPart/ApplicationPartSorter_Tests.cs new file mode 100644 index 0000000000..829aa23527 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ApplicationPart/ApplicationPartSorter_Tests.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using Microsoft.AspNetCore.Mvc.ApplicationParts; +using NSubstitute; +using Shouldly; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationPart; + +public class ApplicationPartSorter_Tests +{ + [Fact] + public void Should_Sort_ApplicationParts_By_Module_Dependencies() + { + var moduleDescriptors = new List(); + var partManager = new ApplicationPartManager(); + + for (var i = 0; i < 10; i++) + { + var assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName($"ModuleA{i}.dll"), AssemblyBuilderAccess.Run); + partManager.ApplicationParts.Add(new AssemblyPart(assembly)); + moduleDescriptors.Add(CreateModuleDescriptor(assembly)); + } + var randomApplicationParts = partManager.ApplicationParts.OrderBy(x => Guid.NewGuid()).ToList(); // Shuffle the parts + + // Additional part + randomApplicationParts.AddFirst(new CompiledRazorAssemblyPart(typeof(AbpAspNetCoreModule).Assembly)); + randomApplicationParts.Insert(5, new CompiledRazorAssemblyPart(typeof(AbpAspNetCoreMvcModule).Assembly)); + randomApplicationParts.AddLast(new CompiledRazorAssemblyPart(typeof(AbpVirtualFileSystemModule).Assembly)); + + partManager.ApplicationParts.Clear(); + foreach (var part in randomApplicationParts) + { + partManager.ApplicationParts.Add(part); + } + + var moduleContainer = CreateFakeModuleContainer(moduleDescriptors); + + ApplicationPartSorter.Sort(partManager, moduleContainer); + + // Act + partManager.ApplicationParts.Count.ShouldBe(13); // 10 modules + 3 additional parts + + var applicationParts = partManager.ApplicationParts.Reverse().ToList(); // Reverse the order to match the expected output + + applicationParts[0].ShouldBeOfType().Assembly.ShouldBe(typeof(AbpAspNetCoreModule).Assembly); + applicationParts[1].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA0"); + applicationParts[2].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA1"); + applicationParts[3].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA2"); + applicationParts[4].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA3"); + applicationParts[5].ShouldBeOfType().Assembly.ShouldBe(typeof(AbpAspNetCoreMvcModule).Assembly); + applicationParts[6].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA4"); + applicationParts[7].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA5"); + applicationParts[8].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA6"); + applicationParts[9].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA7"); + applicationParts[10].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA8"); + applicationParts[11].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA9"); + applicationParts[12].ShouldBeOfType().Assembly.ShouldBe(typeof(AbpVirtualFileSystemModule).Assembly); + } + + private static IModuleContainer CreateFakeModuleContainer(List moduleDescriptors) + { + var fakeModuleContainer = Substitute.For(); + fakeModuleContainer.Modules.Returns(moduleDescriptors); + return fakeModuleContainer; + } + + private static IAbpModuleDescriptor CreateModuleDescriptor(Assembly assembly) + { + var moduleDescriptor = Substitute.For(); + moduleDescriptor.Assembly.Returns(assembly); + return moduleDescriptor; + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController.cs index 8e92558cb8..aa6462996a 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController.cs @@ -2,10 +2,18 @@ namespace Volo.Abp.AspNetCore.Mvc.Security.Headers; +[Route("SecurityHeadersTest")] public class SecurityHeadersTestController : AbpController { + [HttpGet("Get")] public ActionResult Get() { return Content("OK"); } + + [HttpGet("ignored")] + public ActionResult Get_Ignored() + { + return Content("OK"); + } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController_Tests.cs index 336c7fe8db..b71103620b 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController_Tests.cs @@ -15,6 +15,8 @@ public class SecurityHeadersTestController_Tests : AspNetCoreMvcTestBase { options.UseContentSecurityPolicyHeader = true; options.Headers["Referrer-Policy"] = "no-referrer"; + + options.IgnoredScriptNoncePaths.Add("/SecurityHeadersTest/ignored"); }); base.ConfigureServices(services); @@ -27,7 +29,6 @@ public class SecurityHeadersTestController_Tests : AspNetCoreMvcTestBase responseMessage.Headers.ShouldContain(x => x.Key == "X-Content-Type-Options" & x.Value.First().ToString() == "nosniff"); responseMessage.Headers.ShouldContain(x => x.Key == "X-XSS-Protection" & x.Value.First().ToString() == "1; mode=block"); responseMessage.Headers.ShouldContain(x => x.Key == "X-Frame-Options" & x.Value.First().ToString() == "SAMEORIGIN"); - responseMessage.Headers.ShouldContain(x => x.Key == "X-Content-Type-Options" & x.Value.First().ToString() == "nosniff"); } [Fact] @@ -37,4 +38,15 @@ public class SecurityHeadersTestController_Tests : AspNetCoreMvcTestBase responseMessage.Headers.ShouldNotBeEmpty(); responseMessage.Headers.ShouldContain(x => x.Key == "Referrer-Policy" && x.Value.First().ToString() == "no-referrer"); } + + [Fact] + public async Task SecurityHeaders_Should_Be_Ignored() + { + var responseMessage = await GetResponseAsync("/SecurityHeadersTest/ignored"); + responseMessage.Headers.ShouldNotBeEmpty(); + responseMessage.Headers.ShouldNotContain(x => x.Key == "X-Content-Type-Options" & x.Value.First().ToString() == "nosniff"); + responseMessage.Headers.ShouldNotContain(x => x.Key == "X-XSS-Protection" & x.Value.First().ToString() == "1; mode=block"); + responseMessage.Headers.ShouldNotContain(x => x.Key == "X-Frame-Options" & x.Value.First().ToString() == "SAMEORIGIN"); + responseMessage.Headers.ShouldNotContain(x => x.Key == "Referrer-Policy" && x.Value.First().ToString() == "no-referrer"); + } } diff --git a/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Pages/Index.cshtml b/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Pages/Index.cshtml index dc32cca59c..ea4a9f3347 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Pages/Index.cshtml +++ b/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Pages/Index.cshtml @@ -15,17 +15,38 @@ @if (HttpContext.User.Identity != null && HttpContext.User.Identity.IsAuthenticated) {
    +

    Current User

    @foreach (var claim in HttpContext.User.Claims) {
  • @claim.Type : @claim.Value
  • }
+
    +

    oidc

    + @{ + var oidc = await HttpContext.AuthenticateAsync("oidc"); + if (oidc.Principal != null) + { + foreach (var claim in oidc.Principal.Claims) + { +
  • @claim.Type : @claim.Value
  • + } + } + } +
+ +

HttpContext.GetTokenAsync("access_token")
@await HttpContext.GetTokenAsync("access_token")

+

HttpContext.GetTokenAsync("id_token") +
+ @await HttpContext.GetTokenAsync("id_token") +

+ var client = new HttpClient(); var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44303/api/claims"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await HttpContext.GetTokenAsync("access_token")); diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250215074649_Initial.Designer.cs b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.Designer.cs similarity index 99% rename from modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250215074649_Initial.Designer.cs rename to modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.Designer.cs index fec1a9f143..5c4b03ed99 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250215074649_Initial.Designer.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace OpenIddict.Demo.Server.Migrations { [DbContext(typeof(ServerDbContext))] - [Migration("20250215074649_Initial")] + [Migration("20250630055813_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace OpenIddict.Demo.Server.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("ProductVersion", "9.0.5") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -938,6 +938,9 @@ namespace OpenIddict.Demo.Server.Migrations .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); + b.Property("FrontChannelLogoutUri") + .HasColumnType("nvarchar(max)"); + b.Property("IsDeleted") .ValueGeneratedOnAdd() .HasColumnType("bit") diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250215074649_Initial.cs b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.cs similarity index 99% rename from modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250215074649_Initial.cs rename to modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.cs index fdf8dac987..77775a3324 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250215074649_Initial.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.cs @@ -378,6 +378,7 @@ namespace OpenIddict.Demo.Server.Migrations RedirectUris = table.Column(type: "nvarchar(max)", nullable: true), Requirements = table.Column(type: "nvarchar(max)", nullable: true), Settings = table.Column(type: "nvarchar(max)", nullable: true), + FrontChannelLogoutUri = table.Column(type: "nvarchar(max)", nullable: true), ClientUri = table.Column(type: "nvarchar(max)", nullable: true), LogoUri = table.Column(type: "nvarchar(max)", nullable: true), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: false), diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs index 4099fccc43..ff0eaf970c 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace OpenIddict.Demo.Server.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("ProductVersion", "9.0.5") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -935,6 +935,9 @@ namespace OpenIddict.Demo.Server.Migrations .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); + b.Property("FrontChannelLogoutUri") + .HasColumnType("nvarchar(max)"); + b.Property("IsDeleted") .ValueGeneratedOnAdd() .HasColumnType("bit") diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/Pages/Index.cshtml b/modules/openiddict/app/OpenIddict.Demo.Server/Pages/Index.cshtml index fd28464cc7..daa3ee9b95 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/Pages/Index.cshtml +++ b/modules/openiddict/app/OpenIddict.Demo.Server/Pages/Index.cshtml @@ -4,7 +4,13 @@ ViewData["Title"] = "Home page"; } -
-

Welcome

-

Learn about building Web apps with ASP.NET Core.

-
\ No newline at end of file +@if (HttpContext.User.Identity != null && HttpContext.User.Identity.IsAuthenticated) +{ +
    +

    Current User

    + @foreach (var claim in HttpContext.User.Claims) + { +
  • @claim.Type : @claim.Value
  • + } +
+} diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs index 5c2f6ef996..a8350e22e5 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text.Encodings.Web; @@ -148,6 +149,13 @@ public class AuthorizeController : AbpOpenIdDictControllerBase case OpenIddictConstants.ConsentTypes.Explicit when authorizations.Any() && !request.HasPromptValue(OpenIddictConstants.PromptValues.Consent): var principal = await SignInManager.CreateUserPrincipalAsync(user); + var sid = dynamicPrincipal.FindFirst(JwtRegisteredClaimNames.Sid); + if (sid != null) + { + principal.RemoveClaims(JwtRegisteredClaimNames.Sid); + principal.AddClaim(JwtRegisteredClaimNames.Sid, sid.Value); + } + if (result.Properties != null && result.Properties.IsPersistent) { var claim = new Claim(AbpClaimTypes.RememberMe, true.ToString()).SetDestinations(OpenIddictConstants.Destinations.AccessToken); @@ -247,6 +255,13 @@ public class AuthorizeController : AbpOpenIdDictControllerBase var principal = await SignInManager.CreateUserPrincipalAsync(user); + var sid = User.FindFirst(JwtRegisteredClaimNames.Sid); + if (sid != null) + { + principal.RemoveClaims(JwtRegisteredClaimNames.Sid); + principal.AddClaim(JwtRegisteredClaimNames.Sid, sid.Value); + } + var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme); if (result.Succeeded && result.Properties != null && result.Properties.IsPersistent) { diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationDescriptor.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationDescriptor.cs index 3b5a4fc247..5fb2de1205 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationDescriptor.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationDescriptor.cs @@ -1,16 +1,22 @@ -using OpenIddict.Abstractions; +using System; +using OpenIddict.Abstractions; namespace Volo.Abp.OpenIddict.Applications; public class AbpApplicationDescriptor : OpenIddictApplicationDescriptor { + /// + /// Gets or sets the front-channel logout URI associated with the application. + /// + public virtual Uri FrontChannelLogoutUri { get; set; } + /// /// URI to further information about client. /// - public string ClientUri { get; set; } + public virtual string ClientUri { get; set; } /// /// URI to client logo. /// - public string LogoUri { get; set; } + public virtual string LogoUri { get; set; } } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationManager.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationManager.cs index e02d671652..99b44e4293 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationManager.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationManager.cs @@ -44,6 +44,17 @@ public class AbpApplicationManager : OpenIddictApplicationManager GetFrontChannelLogoutUriAsync(object application, CancellationToken cancellationToken = default) + { + Check.NotNull(application, nameof(application)); + Check.AssignableTo(application.GetType(), nameof(application)); + + return await Store.As().GetFrontChannelLogoutUriAsync(application.As(), cancellationToken); + } + public virtual async ValueTask GetClientUriAsync(object application, CancellationToken cancellationToken = default) { Check.NotNull(application, nameof(application)); - Check.AssignableTo(application.GetType(), nameof(application)); + Check.AssignableTo(application.GetType(), nameof(application)); return await Store.As().GetClientUriAsync(application.As(), cancellationToken); } @@ -71,8 +91,15 @@ public class AbpApplicationManager : OpenIddictApplicationManager GetLogoUriAsync(object application, CancellationToken cancellationToken = default) { Check.NotNull(application, nameof(application)); - Check.AssignableTo(application.GetType(), nameof(application)); + Check.AssignableTo(application.GetType(), nameof(application)); return await Store.As().GetLogoUriAsync(application.As(), cancellationToken); } + + protected virtual bool IsImplicitFileUri(Uri uri) + { + Check.NotNull(uri, nameof(uri)); + + return uri.IsAbsoluteUri && uri.IsFile && !uri.OriginalString.StartsWith(uri.Scheme, StringComparison.OrdinalIgnoreCase); + } } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs index 416d90b3b7..f604a0960c 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs @@ -635,6 +635,13 @@ public class AbpOpenIddictApplicationStore : AbpOpenIddictStoreBase GetFrontChannelLogoutUriAsync(OpenIddictApplicationModel application, CancellationToken cancellationToken = default) + { + Check.NotNull(application, nameof(application)); + + return await new ValueTask(application.FrontChannelLogoutUri); + } + public virtual ValueTask GetClientUriAsync(OpenIddictApplicationModel application, CancellationToken cancellationToken = default) { Check.NotNull(application, nameof(application)); diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpApplicationManager.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpApplicationManager.cs index 1f12a9d088..b0dd375908 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpApplicationManager.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpApplicationManager.cs @@ -6,6 +6,8 @@ namespace Volo.Abp.OpenIddict.Applications; public interface IAbpApplicationManager : IOpenIddictApplicationManager { + ValueTask GetFrontChannelLogoutUriAsync(object application, CancellationToken cancellationToken = default); + ValueTask GetClientUriAsync(object application, CancellationToken cancellationToken = default); ValueTask GetLogoUriAsync(object application, CancellationToken cancellationToken = default); diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpOpenIdApplicationStore.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpOpenIdApplicationStore.cs index ca2cd50102..9dd0b70515 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpOpenIdApplicationStore.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpOpenIdApplicationStore.cs @@ -6,6 +6,8 @@ namespace Volo.Abp.OpenIddict.Applications; public interface IAbpOpenIdApplicationStore : IOpenIddictApplicationStore { + ValueTask GetFrontChannelLogoutUriAsync(OpenIddictApplicationModel application, CancellationToken cancellationToken = default); + ValueTask GetClientUriAsync(OpenIddictApplicationModel application, CancellationToken cancellationToken = default); ValueTask GetLogoUriAsync(OpenIddictApplicationModel application, CancellationToken cancellationToken = default); diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplication.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplication.cs index e88370e874..d2f7b5f752 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplication.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplication.cs @@ -94,13 +94,18 @@ public class OpenIddictApplication : FullAuditedAggregateRoot /// public virtual string Settings { get; set; } + /// + /// Gets or sets the front-channel logout URI associated with the application. + /// + public virtual string FrontChannelLogoutUri { get; set; } + /// /// URI to further information about client. /// - public string ClientUri { get; set; } + public virtual string ClientUri { get; set; } /// /// URI to client logo. /// - public string LogoUri { get; set; } + public virtual string LogoUri { get; set; } } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationExtensions.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationExtensions.cs index 791136316a..818a40f973 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationExtensions.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationExtensions.cs @@ -27,6 +27,7 @@ public static class OpenIddictApplicationExtensions RedirectUris = model.RedirectUris, Requirements = model.Requirements, Settings = model.Settings, + FrontChannelLogoutUri = model.FrontChannelLogoutUri, ClientUri = model.ClientUri, LogoUri = model.LogoUri }; @@ -59,6 +60,7 @@ public static class OpenIddictApplicationExtensions entity.RedirectUris = model.RedirectUris; entity.Requirements = model.Requirements; entity.Settings = model.Settings; + entity.FrontChannelLogoutUri = model.FrontChannelLogoutUri; entity.ClientUri = model.ClientUri; entity.LogoUri = model.LogoUri; @@ -100,6 +102,7 @@ public static class OpenIddictApplicationExtensions RedirectUris = entity.RedirectUris, Requirements = entity.Requirements, Settings = entity.Settings, + FrontChannelLogoutUri = entity.FrontChannelLogoutUri, ClientUri = entity.ClientUri, LogoUri = entity.LogoUri }; diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationModel.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationModel.cs index 48a376769f..6841b10a04 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationModel.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationModel.cs @@ -89,13 +89,18 @@ public class OpenIddictApplicationModel : ExtensibleObject /// public virtual string Settings { get; set; } + /// + /// Gets or sets the front-channel logout URI associated with the application. + /// + public virtual string FrontChannelLogoutUri { get; set; } + /// /// URI to further information about client. /// - public string ClientUri { get; set; } + public virtual string ClientUri { get; set; } /// /// URI to client logo. /// - public string LogoUri { get; set; } + public virtual string LogoUri { get; set; } }