Browse Source

Merge pull request #23196 from abpframework/FrontChannelLogoutUri

Add `FrontChannelLogoutUri` property and related methods to OpenIddict application classes
pull/23257/head
Alper Ebiçoğlu 7 months ago
committed by GitHub
parent
commit
387fedd951
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs
  2. 158
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs
  3. 13
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs
  4. 79
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ApplicationPart/ApplicationPartSorter_Tests.cs
  5. 8
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController.cs
  6. 14
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController_Tests.cs
  7. 21
      modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Pages/Index.cshtml
  8. 7
      modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.Designer.cs
  9. 1
      modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.cs
  10. 5
      modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs
  11. 14
      modules/openiddict/app/OpenIddict.Demo.Server/Pages/Index.cshtml
  12. 15
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs
  13. 12
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationDescriptor.cs
  14. 31
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationManager.cs
  15. 7
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs
  16. 2
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpApplicationManager.cs
  17. 2
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpOpenIdApplicationStore.cs
  18. 9
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplication.cs
  19. 3
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationExtensions.cs
  20. 9
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationModel.cs

6
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<AbpSecurityHeadersOptions>(options =>
{
options.IgnoredScriptNoncePaths.Add("/signout-oidc");
});
}
}

158
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<ApplicationPart, List<ApplicationPart>> CreateDependencyDictionary(
ApplicationPartManager partManager, IModuleContainer moduleContainer)
{
var dependencyDictionary = new Dictionary<ApplicationPart, List<ApplicationPart>>();
foreach (var applicationPart in partManager.ApplicationParts)
{
dependencyDictionary[applicationPart] =
CreateDependencyList(applicationPart, partManager, moduleContainer);
}
return dependencyDictionary;
}
private static List<ApplicationPart> CreateDependencyList(
ApplicationPart applicationPart,
ApplicationPartManager partManager,
IModuleContainer moduleContainer)
private static Assembly GetApplicationPartAssembly(ApplicationPart part)
{
var list = new List<ApplicationPart>();
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<ApplicationPart> 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<ApplicationPart> 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<AssemblyPart>();
}
var moduleDependedAssemblies = moduleDescriptor
.Dependencies
.SelectMany(d => d.AllAssemblies)
.ToArray();
return partManager.ApplicationParts
.OfType<AssemblyPart>()
.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<CompiledRazorAssemblyPart>()
.FirstOrDefault(p => p.Name == viewsAssemblyName);
}
private static AssemblyPart? GetOriginalAssemblyPartOrNull(
CompiledRazorAssemblyPart compiledRazorAssemblyPart,
ApplicationPartManager partManager)
{
var originalAssemblyName = compiledRazorAssemblyPart.Name.RemovePostFix(".Views");
return partManager.ApplicationParts
.OfType<AssemblyPart>()
.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")
};
}
}

13
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<IgnoreAbpSecurityHeaderAttribute>() != null)
if (endpoint?.Metadata.GetMetadata<IgnoreAbpSecurityHeaderAttribute>() != 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"))

79
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<IAbpModuleDescriptor>();
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<CompiledRazorAssemblyPart>().Assembly.ShouldBe(typeof(AbpAspNetCoreModule).Assembly);
applicationParts[1].ShouldBeOfType<AssemblyPart>().Assembly.GetName().Name.ShouldStartWith("ModuleA0");
applicationParts[2].ShouldBeOfType<AssemblyPart>().Assembly.GetName().Name.ShouldStartWith("ModuleA1");
applicationParts[3].ShouldBeOfType<AssemblyPart>().Assembly.GetName().Name.ShouldStartWith("ModuleA2");
applicationParts[4].ShouldBeOfType<AssemblyPart>().Assembly.GetName().Name.ShouldStartWith("ModuleA3");
applicationParts[5].ShouldBeOfType<CompiledRazorAssemblyPart>().Assembly.ShouldBe(typeof(AbpAspNetCoreMvcModule).Assembly);
applicationParts[6].ShouldBeOfType<AssemblyPart>().Assembly.GetName().Name.ShouldStartWith("ModuleA4");
applicationParts[7].ShouldBeOfType<AssemblyPart>().Assembly.GetName().Name.ShouldStartWith("ModuleA5");
applicationParts[8].ShouldBeOfType<AssemblyPart>().Assembly.GetName().Name.ShouldStartWith("ModuleA6");
applicationParts[9].ShouldBeOfType<AssemblyPart>().Assembly.GetName().Name.ShouldStartWith("ModuleA7");
applicationParts[10].ShouldBeOfType<AssemblyPart>().Assembly.GetName().Name.ShouldStartWith("ModuleA8");
applicationParts[11].ShouldBeOfType<AssemblyPart>().Assembly.GetName().Name.ShouldStartWith("ModuleA9");
applicationParts[12].ShouldBeOfType<CompiledRazorAssemblyPart>().Assembly.ShouldBe(typeof(AbpVirtualFileSystemModule).Assembly);
}
private static IModuleContainer CreateFakeModuleContainer(List<IAbpModuleDescriptor> moduleDescriptors)
{
var fakeModuleContainer = Substitute.For<IModuleContainer>();
fakeModuleContainer.Modules.Returns(moduleDescriptors);
return fakeModuleContainer;
}
private static IAbpModuleDescriptor CreateModuleDescriptor(Assembly assembly)
{
var moduleDescriptor = Substitute.For<IAbpModuleDescriptor>();
moduleDescriptor.Assembly.Returns(assembly);
return moduleDescriptor;
}
}

8
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");
}
}

14
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");
}
}

21
modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Pages/Index.cshtml

@ -15,17 +15,38 @@
@if (HttpContext.User.Identity != null && HttpContext.User.Identity.IsAuthenticated)
{
<ul class="list-group mt-3 text-start">
<p>Current User</p>
@foreach (var claim in HttpContext.User.Claims)
{
<li class="list-group-item">@claim.Type : @claim.Value</li>
}
</ul>
<ul class="list-group mt-3 text-start">
<p>oidc</p>
@{
var oidc = await HttpContext.AuthenticateAsync("oidc");
if (oidc.Principal != null)
{
foreach (var claim in oidc.Principal.Claims)
{
<li class="list-group-item">@claim.Type : @claim.Value</li>
}
}
}
</ul>
<p>HttpContext.GetTokenAsync("access_token")
<br/>
@await HttpContext.GetTokenAsync("access_token")
</p>
<p>HttpContext.GetTokenAsync("id_token")
<br/>
@await HttpContext.GetTokenAsync("id_token")
</p>
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"));

7
modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250215074649_Initial.Designer.cs → 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
{
/// <inheritdoc />
@ -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<string>("FrontChannelLogoutUri")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")

1
modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250215074649_Initial.cs → modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.cs

@ -378,6 +378,7 @@ namespace OpenIddict.Demo.Server.Migrations
RedirectUris = table.Column<string>(type: "nvarchar(max)", nullable: true),
Requirements = table.Column<string>(type: "nvarchar(max)", nullable: true),
Settings = table.Column<string>(type: "nvarchar(max)", nullable: true),
FrontChannelLogoutUri = table.Column<string>(type: "nvarchar(max)", nullable: true),
ClientUri = table.Column<string>(type: "nvarchar(max)", nullable: true),
LogoUri = table.Column<string>(type: "nvarchar(max)", nullable: true),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: false),

5
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<string>("FrontChannelLogoutUri")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")

14
modules/openiddict/app/OpenIddict.Demo.Server/Pages/Index.cshtml

@ -4,7 +4,13 @@
ViewData["Title"] = "Home page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
@if (HttpContext.User.Identity != null && HttpContext.User.Identity.IsAuthenticated)
{
<ul class="list-group mt-3 text-start">
<p>Current User</p>
@foreach (var claim in HttpContext.User.Claims)
{
<li class="list-group-item">@claim.Type : @claim.Value</li>
}
</ul>
}

15
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)
{

12
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
{
/// <summary>
/// Gets or sets the front-channel logout URI associated with the application.
/// </summary>
public virtual Uri FrontChannelLogoutUri { get; set; }
/// <summary>
/// URI to further information about client.
/// </summary>
public string ClientUri { get; set; }
public virtual string ClientUri { get; set; }
/// <summary>
/// URI to client logo.
/// </summary>
public string LogoUri { get; set; }
public virtual string LogoUri { get; set; }
}

31
modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationManager.cs

@ -44,6 +44,17 @@ public class AbpApplicationManager : OpenIddictApplicationManager<OpenIddictAppl
if (descriptor is AbpApplicationDescriptor model)
{
if (!application.FrontChannelLogoutUri.IsNullOrWhiteSpace())
{
if (!Uri.TryCreate(application.FrontChannelLogoutUri, UriKind.Absolute, out var uri) || IsImplicitFileUri(uri))
{
throw new ArgumentException(OpenIddictResources.GetResourceString("ID0214"));
}
model.FrontChannelLogoutUri = uri;
}
model.ClientUri = application.ClientUri;
model.LogoUri = application.LogoUri;
}
@ -55,15 +66,24 @@ public class AbpApplicationManager : OpenIddictApplicationManager<OpenIddictAppl
if (descriptor is AbpApplicationDescriptor model)
{
application.FrontChannelLogoutUri = model.FrontChannelLogoutUri?.OriginalString;
application.ClientUri = model.ClientUri;
application.LogoUri = model.LogoUri;
}
}
public virtual async ValueTask<string> GetFrontChannelLogoutUriAsync(object application, CancellationToken cancellationToken = default)
{
Check.NotNull(application, nameof(application));
Check.AssignableTo<OpenIddictApplicationModel>(application.GetType(), nameof(application));
return await Store.As<IAbpOpenIdApplicationStore>().GetFrontChannelLogoutUriAsync(application.As<OpenIddictApplicationModel>(), cancellationToken);
}
public virtual async ValueTask<string> GetClientUriAsync(object application, CancellationToken cancellationToken = default)
{
Check.NotNull(application, nameof(application));
Check.AssignableTo<IAbpOpenIdApplicationStore>(application.GetType(), nameof(application));
Check.AssignableTo<OpenIddictApplicationModel>(application.GetType(), nameof(application));
return await Store.As<IAbpOpenIdApplicationStore>().GetClientUriAsync(application.As<OpenIddictApplicationModel>(), cancellationToken);
}
@ -71,8 +91,15 @@ public class AbpApplicationManager : OpenIddictApplicationManager<OpenIddictAppl
public virtual async ValueTask<string> GetLogoUriAsync(object application, CancellationToken cancellationToken = default)
{
Check.NotNull(application, nameof(application));
Check.AssignableTo<IAbpOpenIdApplicationStore>(application.GetType(), nameof(application));
Check.AssignableTo<OpenIddictApplicationModel>(application.GetType(), nameof(application));
return await Store.As<IAbpOpenIdApplicationStore>().GetLogoUriAsync(application.As<OpenIddictApplicationModel>(), cancellationToken);
}
protected virtual bool IsImplicitFileUri(Uri uri)
{
Check.NotNull(uri, nameof(uri));
return uri.IsAbsoluteUri && uri.IsFile && !uri.OriginalString.StartsWith(uri.Scheme, StringComparison.OrdinalIgnoreCase);
}
}

7
modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs

@ -635,6 +635,13 @@ public class AbpOpenIddictApplicationStore : AbpOpenIddictStoreBase<IOpenIddictA
}
}
public virtual async ValueTask<string> GetFrontChannelLogoutUriAsync(OpenIddictApplicationModel application, CancellationToken cancellationToken = default)
{
Check.NotNull(application, nameof(application));
return await new ValueTask<string>(application.FrontChannelLogoutUri);
}
public virtual ValueTask<string> GetClientUriAsync(OpenIddictApplicationModel application, CancellationToken cancellationToken = default)
{
Check.NotNull(application, nameof(application));

2
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<string> GetFrontChannelLogoutUriAsync(object application, CancellationToken cancellationToken = default);
ValueTask<string> GetClientUriAsync(object application, CancellationToken cancellationToken = default);
ValueTask<string> GetLogoUriAsync(object application, CancellationToken cancellationToken = default);

2
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<OpenIddictApplicationModel>
{
ValueTask<string> GetFrontChannelLogoutUriAsync(OpenIddictApplicationModel application, CancellationToken cancellationToken = default);
ValueTask<string> GetClientUriAsync(OpenIddictApplicationModel application, CancellationToken cancellationToken = default);
ValueTask<string> GetLogoUriAsync(OpenIddictApplicationModel application, CancellationToken cancellationToken = default);

9
modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplication.cs

@ -94,13 +94,18 @@ public class OpenIddictApplication : FullAuditedAggregateRoot<Guid>
/// </summary>
public virtual string Settings { get; set; }
/// <summary>
/// Gets or sets the front-channel logout URI associated with the application.
/// </summary>
public virtual string FrontChannelLogoutUri { get; set; }
/// <summary>
/// URI to further information about client.
/// </summary>
public string ClientUri { get; set; }
public virtual string ClientUri { get; set; }
/// <summary>
/// URI to client logo.
/// </summary>
public string LogoUri { get; set; }
public virtual string LogoUri { get; set; }
}

3
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
};

9
modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationModel.cs

@ -89,13 +89,18 @@ public class OpenIddictApplicationModel : ExtensibleObject
/// </summary>
public virtual string Settings { get; set; }
/// <summary>
/// Gets or sets the front-channel logout URI associated with the application.
/// </summary>
public virtual string FrontChannelLogoutUri { get; set; }
/// <summary>
/// URI to further information about client.
/// </summary>
public string ClientUri { get; set; }
public virtual string ClientUri { get; set; }
/// <summary>
/// URI to client logo.
/// </summary>
public string LogoUri { get; set; }
public virtual string LogoUri { get; set; }
}

Loading…
Cancel
Save