Browse Source

Merge pull request #18064 from abpframework/dynamic-claims-middleware

Dynamic claims middleware
pull/18160/head
Halil İbrahim Kalkan 3 years ago
committed by GitHub
parent
commit
c912fc81c6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 80
      docs/en/Dynamic-Claims.md
  2. 8
      docs/en/docs-nav.json
  3. 7
      framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpComponentsClaimsCache.cs
  4. 44
      framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributor.cs
  5. 76
      framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs
  6. 6
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs
  7. 5
      framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs
  8. 25
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpDynamicClaimsMiddleware.cs
  9. 14
      framework/src/Volo.Abp.Security/System/Security/Principal/AbpClaimsIdentityExtensions.cs
  10. 8
      framework/src/Volo.Abp.Security/Volo/Abp/Security/AbpSecurityModule.cs
  11. 10
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimsPrincipalContributorContext.cs
  12. 29
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimsPrincipalFactory.cs
  13. 40
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimsPrincipalFactoryOptions.cs
  14. 17
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaim.cs
  15. 25
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaimCacheItem.cs
  16. 44
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaimsPrincipalContributorBase.cs
  17. 2
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/IAbpClaimsPrincipalFactory.cs
  18. 8
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/IAbpDynamicClaimsPrincipalContributor.cs
  19. 103
      framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/AbpClaimsPrincipalFactory_Test.cs
  20. 189
      framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/AbpClaimsPrincipalFactory_Tests.cs
  21. 76
      framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/AbpDynamicClaimsPrincipalContributorBase_Tests.cs
  22. 4
      framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/CurrentPrincipalAccessor_Tests.cs
  23. 10
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/IDynamicClaimsAppService.cs
  24. 31
      modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/DynamicClaimsAppService.cs
  25. 24
      modules/account/src/Volo.Abp.Account.HttpApi.Client/ClientProxies/Volo/Abp/Account/DynamicClaimsClientProxy.Generated.cs
  26. 7
      modules/account/src/Volo.Abp.Account.HttpApi.Client/ClientProxies/Volo/Abp/Account/DynamicClaimsClientProxy.cs
  27. 41
      modules/account/src/Volo.Abp.Account.HttpApi.Client/ClientProxies/account-generate-proxy.json
  28. 27
      modules/account/src/Volo.Abp.Account.HttpApi/Volo/Abp/Account/DynamicClaimsController.cs
  29. 14
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs
  30. 5
      modules/account/src/Volo.Abp.Account.Web.OpenIddict/Pages/Account/OpenIddictSupportedLoginModel.cs
  31. 15
      modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs
  32. 22
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs
  33. 11
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs
  34. 15
      modules/account/src/Volo.Abp.Account.Web/wwwroot/client-proxies/account-proxy.js
  35. 2
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpUserClaimsPrincipalFactory.cs
  36. 39
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributor.cs
  37. 86
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributorCache.cs
  38. 13
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributorCacheOptions.cs
  39. 40
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserUpdatedEventHandler.cs
  40. 8
      modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpUserClaimsPrincipalFactory_Tests.cs
  41. 59
      modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributor_Tests.cs
  42. 1
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs
  43. 18
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Claims/AbpDynamicClaimsOpenIddictClaimsPrincipalHandler.cs
  44. 2
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AbpOpenIdDictControllerBase.cs
  45. 14
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs
  46. 1
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.AuthorizationCode.cs
  47. 1
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.DeviceCode.cs
  48. 4
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs
  49. 1
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.RefreshToken.cs
  50. 6
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameModule.cs
  51. 6
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameModule.cs
  52. 6
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/MyProjectNameHostModule.cs
  53. 6
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/MyProjectNameHostModule.cs
  54. 6
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameModule.cs
  55. 6
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameModule.cs
  56. 6
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameModule.cs
  57. 6
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameModule.cs
  58. 7
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyProjectNameAuthServerModule.cs
  59. 11
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBlazorModule.cs
  60. 7
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBlazorModule.cs
  61. 7
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyProjectNameBlazorModule.cs
  62. 9
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs
  63. 7
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs
  64. 1
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj
  65. 10
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs
  66. 6
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs

80
docs/en/Dynamic-Claims.md

@ -0,0 +1,80 @@
# Dynamic Claims
## What is Dynamic Claims and Why do we need it
We use claims-based authentication in ASP.NET Core, It will be store the claims in the cookie or token. But the claims are static, it will be not change after the user re-login. If the user changed its username or role, we still get the old claims.
The `Dynamic Claims` feature is used to dynamically generate claims for the user in each request. You can always get the latest user claims.
## How to use it
This feature is disabled by default. You can enable it by following code:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
});
}
````
If you are using the tiered solution you need to set the `RemoteRefreshUrl` to the Auth Server url in the UI project.
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
options.RemoteRefreshUrl = configuration["AuthServerUrl"] + options.RemoteRefreshUrl;
});
}
````
Then add the `DynamicClaims` middleware.
````csharp
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
// Add this line before UseAuthorization.
app.UseDynamicClaims();
app.UseAuthorization();
//...
}
````
## How it works
The `DynamicClaims` middleware will use `IAbpClaimsPrincipalFactory` to dynamically generate claims for the current user(`HttpContext.User`) in each request.
There are two implementations of `IAbpDynamicClaimsPrincipalContributor` for different scenarios.
### IdentityDynamicClaimsPrincipalContributor
This implementation is used for the `Monolithic` solution. It will get the dynamic claims from the `IUserClaimsPrincipalFactory` and add/replace the current user claims.
It uses cache to improve performance. the cache will be invalidated when the user entity changed.
### RemoteDynamicClaimsPrincipalContributor
This implementation is used for the `Tiered` solution. It will get the dynamic claims from the cache of the Auth Server. It will call the `RemoteRefreshUrl` of the Auth Server to refresh the cache when the cache is invalid.
## IAbpDynamicClaimsPrincipalContributor
If you want to add your own dynamic claims contributor, you can a class that implement the `IAbpDynamicClaimsPrincipalContributor` interface. The framework will call the `ContributeAsync` method when get the dynamic claims.
> It better to use cache to improve performance.
## AbpClaimsPrincipalFactoryOptions
* `IsDynamicClaimsEnabled`: Enable or disable the dynamic claims feature.
* `RemoteRefreshUrl`: The url of the Auth Server to refresh the cache. It will be used by the `RemoteDynamicClaimsPrincipalContributor`. The default value is `/api/account/dynamic-claims/refresh`.
* `DynamicClaims`: A list of dynamic claim types, `DynamicClaims contributor`` will only handle the claim type in this list.
* `ClaimsMap`: A dictionary to map the claim types. This is used when the claim types are different between the Auth Server and the client. Already set up for common claim types by default
## See Also
* [Authorization](Authorization.md)
* [Claims-based authorization in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/security/authorization/claims)
* [Mapping, customizing, and transforming claims in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/claims)

8
docs/en/docs-nav.json

@ -178,7 +178,13 @@
},
{
"text": "Authorization",
"path": "Authorization.md"
"path": "Authorization.md",
"items": [
{
"text": "Dynamic Claims",
"path": "Dynamic-Claims.md"
}
]
},
{
"text": "Caching",

7
framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpComponentsClaimsCache.cs

@ -1,9 +1,9 @@
using System.Security.Claims;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Components.Web.Security;
@ -12,16 +12,18 @@ public class AbpComponentsClaimsCache : IScopedDependency
public ClaimsPrincipal Principal { get; private set; } = default!;
private readonly AuthenticationStateProvider? _authenticationStateProvider;
private readonly IAbpClaimsPrincipalFactory _abpClaimsPrincipalFactory;
public AbpComponentsClaimsCache(
IClientScopeServiceProviderAccessor serviceProviderAccessor)
{
_authenticationStateProvider = serviceProviderAccessor.ServiceProvider.GetService<AuthenticationStateProvider>();
_abpClaimsPrincipalFactory = serviceProviderAccessor.ServiceProvider.GetRequiredService<IAbpClaimsPrincipalFactory>();
if (_authenticationStateProvider != null)
{
_authenticationStateProvider.AuthenticationStateChanged += async (task) =>
{
Principal = (await task).User;
Principal = await _abpClaimsPrincipalFactory.CreateDynamicAsync((await task).User);
};
}
}
@ -32,6 +34,7 @@ public class AbpComponentsClaimsCache : IScopedDependency
{
var authenticationState = await _authenticationStateProvider.GetAuthenticationStateAsync();
Principal = authenticationState.User;
await _abpClaimsPrincipalFactory.CreateDynamicAsync(Principal);
}
}
}

44
framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributor.cs

@ -0,0 +1,44 @@
using System;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Mvc.Client;
public class RemoteDynamicClaimsPrincipalContributor : AbpDynamicClaimsPrincipalContributorBase
{
public async override Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var identity = context.ClaimsPrincipal.Identities.FirstOrDefault();
if (identity == null)
{
return;
}
var userId = identity.FindUserId();
if (userId == null)
{
return;
}
var dynamicClaimsCache = context.GetRequiredService<RemoteDynamicClaimsPrincipalContributorCache>();
AbpDynamicClaimCacheItem dynamicClaims;
try
{
dynamicClaims = await dynamicClaimsCache.GetAsync(userId.Value, identity.FindTenantId());
}
catch (Exception e)
{
// In case if failed refresh remote dynamic cache, We force to clear the claims principal.
context.ClaimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity());
var logger = context.GetRequiredService<ILogger<RemoteDynamicClaimsPrincipalContributor>>();
logger.LogWarning(e, $"Failed to refresh remote dynamic claims cache for user: {userId.Value}");
return;
}
await AddDynamicClaimsAsync(context, identity, dynamicClaims.Claims);
}
}

76
framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.Authentication;
using Volo.Abp.Json;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Mvc.Client;
public class RemoteDynamicClaimsPrincipalContributorCache : ITransientDependency
{
public const string HttpClientName = nameof(RemoteDynamicClaimsPrincipalContributorCache);
public ILogger<RemoteDynamicClaimsPrincipalContributorCache> Logger { get; set; }
protected IDistributedCache<AbpDynamicClaimCacheItem> Cache { get; }
protected IHttpClientFactory HttpClientFactory { get; }
protected IOptions<AbpClaimsPrincipalFactoryOptions> AbpClaimsPrincipalFactoryOptions { get; }
protected IJsonSerializer JsonSerializer { get; }
protected IRemoteServiceHttpClientAuthenticator HttpClientAuthenticator { get; }
public RemoteDynamicClaimsPrincipalContributorCache(
IDistributedCache<AbpDynamicClaimCacheItem> cache,
IHttpClientFactory httpClientFactory,
IOptions<AbpClaimsPrincipalFactoryOptions> abpClaimsPrincipalFactoryOptions,
IJsonSerializer jsonSerializer,
IRemoteServiceHttpClientAuthenticator httpClientAuthenticator)
{
Cache = cache;
HttpClientFactory = httpClientFactory;
AbpClaimsPrincipalFactoryOptions = abpClaimsPrincipalFactoryOptions;
JsonSerializer = jsonSerializer;
HttpClientAuthenticator = httpClientAuthenticator;
Logger = NullLogger<RemoteDynamicClaimsPrincipalContributorCache>.Instance;
}
public virtual async Task<AbpDynamicClaimCacheItem> GetAsync(Guid userId, Guid? tenantId = null)
{
Logger.LogDebug($"Get dynamic claims cache for user: {userId}");
var dynamicClaims = await Cache.GetAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId));
if (dynamicClaims != null && !dynamicClaims.Claims.IsNullOrEmpty())
{
return dynamicClaims;
}
Logger.LogDebug($"Refresh dynamic claims for user: {userId} from remote service.");
try
{
var client = HttpClientFactory.CreateClient(HttpClientName);
var requestMessage = new HttpRequestMessage(HttpMethod.Post, AbpClaimsPrincipalFactoryOptions.Value.RemoteRefreshUrl);
await HttpClientAuthenticator.Authenticate(new RemoteServiceHttpClientAuthenticateContext(client, requestMessage, new RemoteServiceConfiguration("/"), string.Empty));
var response = await client.SendAsync(requestMessage);
response.EnsureSuccessStatusCode();
}
catch (Exception e)
{
Logger.LogWarning(e, $"Failed to refresh remote claims for user: {userId}");
throw;
}
dynamicClaims = await Cache.GetAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId));
if (dynamicClaims == null || dynamicClaims.Claims.IsNullOrEmpty())
{
throw new AbpException($"Failed to refresh remote claims for user: {userId}");
}
return dynamicClaims!;
}
}

6
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
@ -61,12 +61,12 @@ public abstract class ChallengeAccountController : AbpController
}
[HttpGet]
public virtual Task<IActionResult> AccessDenied(string returnUrl = "", string returnUrlHash = "")
public virtual Task<IActionResult> AccessDenied()
{
return Task.FromResult<IActionResult>(Challenge(
new AuthenticationProperties
{
RedirectUri = GetRedirectUrl(returnUrl, returnUrlHash)
RedirectUri = "/"
},
(ForbidSchemes.IsNullOrEmpty()
? new[]

5
framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs

@ -111,4 +111,9 @@ public static class AbpApplicationBuilderExtensions
{
return app.UseMiddleware<AbpSecurityHeadersMiddleware>();
}
public static IApplicationBuilder UseDynamicClaims(this IApplicationBuilder app)
{
return app.UseMiddleware<AbpDynamicClaimsMiddleware>();
}
}

25
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpDynamicClaimsMiddleware.cs

@ -0,0 +1,25 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Security.Claims;
public class AbpDynamicClaimsMiddleware : IMiddleware, ITransientDependency
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (context.User.Identity?.IsAuthenticated == true)
{
if (context.RequestServices.GetRequiredService<IOptions<AbpClaimsPrincipalFactoryOptions>>().Value.IsDynamicClaimsEnabled)
{
var abpClaimsPrincipalFactory = context.RequestServices.GetRequiredService<IAbpClaimsPrincipalFactory>();
context.User = await abpClaimsPrincipalFactory.CreateDynamicAsync(context.User);
}
}
await next(context);
}
}

14
framework/src/Volo.Abp.Security/System/Security/Principal/AbpClaimsIdentityExtensions.cs

@ -238,6 +238,19 @@ public static class AbpClaimsIdentityExtensions
return claimsIdentity;
}
public static ClaimsIdentity RemoveAll(this ClaimsIdentity claimsIdentity, string claimType)
{
Check.NotNull(claimsIdentity, nameof(claimsIdentity));
foreach (var x in claimsIdentity.FindAll(claimType).ToList())
{
claimsIdentity.RemoveClaim(x);
}
return claimsIdentity;
}
public static ClaimsIdentity AddOrReplace(this ClaimsIdentity claimsIdentity, Claim claim)
{
Check.NotNull(claimsIdentity, nameof(claimsIdentity));
@ -263,5 +276,4 @@ public static class AbpClaimsIdentityExtensions
return principal;
}
}

8
framework/src/Volo.Abp.Security/Volo/Abp/Security/AbpSecurityModule.cs

@ -59,9 +59,11 @@ public class AbpSecurityModule : AbpModule
});
}
private static void AutoAddClaimsPrincipalContributors(IServiceCollection services)
{
var contributorTypes = new List<Type>();
var dynamicContributorTypes = new List<Type>();
services.OnRegistered(context =>
{
@ -69,11 +71,17 @@ public class AbpSecurityModule : AbpModule
{
contributorTypes.Add(context.ImplementationType);
}
if (typeof(IAbpDynamicClaimsPrincipalContributor).IsAssignableFrom(context.ImplementationType))
{
dynamicContributorTypes.Add(context.ImplementationType);
}
});
services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.Contributors.AddIfNotContains(contributorTypes);
options.DynamicContributors.AddIfNotContains(dynamicContributorTypes);
});
}
}

10
framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimsPrincipalContributorContext.cs

@ -1,13 +1,14 @@
using System;
using System.Security.Claims;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Volo.Abp.Security.Claims;
public class AbpClaimsPrincipalContributorContext
{
[NotNull]
public ClaimsPrincipal ClaimsPrincipal { get; }
public ClaimsPrincipal ClaimsPrincipal { get; set; }
[NotNull]
public IServiceProvider ServiceProvider { get; }
@ -19,4 +20,11 @@ public class AbpClaimsPrincipalContributorContext
ClaimsPrincipal = claimsIdentity;
ServiceProvider = serviceProvider;
}
public virtual T GetRequiredService<T>()
where T : notnull
{
Check.NotNull(ServiceProvider, nameof(ServiceProvider));
return ServiceProvider.GetRequiredService<T>();
}
}

29
framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimsPrincipalFactory.cs

@ -22,6 +22,16 @@ public class AbpClaimsPrincipalFactory : IAbpClaimsPrincipalFactory, ITransientD
}
public virtual async Task<ClaimsPrincipal> CreateAsync(ClaimsPrincipal? existsClaimsPrincipal = null)
{
return await InternalCreateAsync(Options, existsClaimsPrincipal, false);
}
public virtual async Task<ClaimsPrincipal> CreateDynamicAsync(ClaimsPrincipal? existsClaimsPrincipal = null)
{
return await InternalCreateAsync(Options, existsClaimsPrincipal, true);
}
public virtual async Task<ClaimsPrincipal> InternalCreateAsync(AbpClaimsPrincipalFactoryOptions options, ClaimsPrincipal? existsClaimsPrincipal = null, bool isDynamic = false)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
@ -32,13 +42,24 @@ public class AbpClaimsPrincipalFactory : IAbpClaimsPrincipalFactory, ITransientD
var context = new AbpClaimsPrincipalContributorContext(claimsPrincipal, scope.ServiceProvider);
foreach (var contributorType in Options.Contributors)
if (!isDynamic)
{
foreach (var contributorType in options.Contributors)
{
var contributor = (IAbpClaimsPrincipalContributor)scope.ServiceProvider.GetRequiredService(contributorType);
await contributor.ContributeAsync(context);
}
}
else
{
var contributor = (IAbpClaimsPrincipalContributor)scope.ServiceProvider.GetRequiredService(contributorType);
await contributor.ContributeAsync(context);
foreach (var contributorType in options.DynamicContributors)
{
var contributor = (IAbpDynamicClaimsPrincipalContributor)scope.ServiceProvider.GetRequiredService(contributorType);
await contributor.ContributeAsync(context);
}
}
return claimsPrincipal;
return context.ClaimsPrincipal;
}
}
}

40
framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimsPrincipalFactoryOptions.cs

@ -1,4 +1,6 @@
using Volo.Abp.Collections;
using System.Collections.Generic;
using System.Security.Claims;
using Volo.Abp.Collections;
namespace Volo.Abp.Security.Claims;
@ -6,8 +8,44 @@ public class AbpClaimsPrincipalFactoryOptions
{
public ITypeList<IAbpClaimsPrincipalContributor> Contributors { get; }
public ITypeList<IAbpDynamicClaimsPrincipalContributor> DynamicContributors { get; }
public List<string> DynamicClaims { get; }
public string RemoteRefreshUrl { get; set; }
public Dictionary<string, List<string>> ClaimsMap { get; set; }
public bool IsDynamicClaimsEnabled { get; set; }
public AbpClaimsPrincipalFactoryOptions()
{
Contributors = new TypeList<IAbpClaimsPrincipalContributor>();
DynamicContributors = new TypeList<IAbpDynamicClaimsPrincipalContributor>();
DynamicClaims = new List<string>
{
AbpClaimTypes.UserName,
AbpClaimTypes.Name,
AbpClaimTypes.SurName,
AbpClaimTypes.Role,
AbpClaimTypes.Email,
AbpClaimTypes.EmailVerified,
AbpClaimTypes.PhoneNumber,
AbpClaimTypes.PhoneNumberVerified
};
RemoteRefreshUrl = "/api/account/dynamic-claims/refresh";
ClaimsMap = new Dictionary<string, List<string>>()
{
{ AbpClaimTypes.UserName, new List<string> { "preferred_username", "unique_name", ClaimTypes.Name }},
{ AbpClaimTypes.Name, new List<string> { "given_name", ClaimTypes.GivenName }},
{ AbpClaimTypes.SurName, new List<string> { "family_name", ClaimTypes.Surname }},
{ AbpClaimTypes.Role, new List<string> { "role", "roles", ClaimTypes.Role }},
{ AbpClaimTypes.Email, new List<string> { "email", ClaimTypes.Email }},
};
IsDynamicClaimsEnabled = false;
}
}

17
framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaim.cs

@ -0,0 +1,17 @@
using System;
namespace Volo.Abp.Security.Claims;
[Serializable]
public class AbpDynamicClaim
{
public string Type { get; set; }
public string? Value { get; set; }
public AbpDynamicClaim(string type, string? value)
{
Type = type;
Value = value;
}
}

25
framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaimCacheItem.cs

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.Security.Claims;
[Serializable]
public class AbpDynamicClaimCacheItem
{
public List<AbpDynamicClaim> Claims { get; set; }
public AbpDynamicClaimCacheItem()
{
Claims = new List<AbpDynamicClaim>();
}
public AbpDynamicClaimCacheItem(List<AbpDynamicClaim> claims)
{
Claims = claims;
}
public static string CalculateCacheKey(Guid userId, Guid? tenantId)
{
return $"{tenantId}-{userId}";
}
}

44
framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpDynamicClaimsPrincipalContributorBase.cs

@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Security.Claims;
public abstract class AbpDynamicClaimsPrincipalContributorBase : IAbpDynamicClaimsPrincipalContributor, ITransientDependency
{
public abstract Task ContributeAsync(AbpClaimsPrincipalContributorContext context);
protected virtual async Task AddDynamicClaimsAsync(AbpClaimsPrincipalContributorContext context, ClaimsIdentity identity, List<AbpDynamicClaim> dynamicClaims)
{
var options = context.GetRequiredService<IOptions<AbpClaimsPrincipalFactoryOptions>>().Value;
foreach (var map in options.ClaimsMap)
{
await MapClaimAsync(identity, dynamicClaims, map.Key, map.Value.ToArray());
}
foreach (var claimGroup in dynamicClaims.GroupBy(x => x.Type))
{
identity.RemoveAll(claimGroup.First().Type);
identity.AddClaims(claimGroup.Where(c => c.Value != null).Select(c => new Claim(claimGroup.First().Type, c.Value!)));
}
}
protected virtual Task MapClaimAsync(ClaimsIdentity identity, List<AbpDynamicClaim> dynamicClaims, string targetClaimType, params string[] sourceClaimTypes)
{
var claims = dynamicClaims.Where(c => sourceClaimTypes.Contains(c.Type)).ToList();
if (claims.IsNullOrEmpty())
{
return Task.CompletedTask;
}
dynamicClaims.RemoveAll(claims);
identity.RemoveAll(targetClaimType);
identity.AddClaims(claims.Where(c => c.Value != null).Select(c => new Claim(targetClaimType, c.Value!)));
return Task.CompletedTask;;
}
}

2
framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/IAbpClaimsPrincipalFactory.cs

@ -6,4 +6,6 @@ namespace Volo.Abp.Security.Claims;
public interface IAbpClaimsPrincipalFactory
{
Task<ClaimsPrincipal> CreateAsync(ClaimsPrincipal? existsClaimsPrincipal = null);
Task<ClaimsPrincipal> CreateDynamicAsync(ClaimsPrincipal? existsClaimsPrincipal = null);
}

8
framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/IAbpDynamicClaimsPrincipalContributor.cs

@ -0,0 +1,8 @@
using System.Threading.Tasks;
namespace Volo.Abp.Security.Claims;
public interface IAbpDynamicClaimsPrincipalContributor
{
Task ContributeAsync(AbpClaimsPrincipalContributorContext context);
}

103
framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/AbpClaimsPrincipalFactory_Test.cs

@ -1,103 +0,0 @@
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Testing;
using Xunit;
namespace Volo.Abp.Security.Claims;
public class AbpClaimsPrincipalFactory_Test : AbpIntegratedTest<AbpSecurityTestModule>
{
private readonly IAbpClaimsPrincipalFactory _abpClaimsPrincipalFactory;
private static string TestAuthenticationType => "Identity.Application";
public AbpClaimsPrincipalFactory_Test()
{
_abpClaimsPrincipalFactory = GetRequiredService<IAbpClaimsPrincipalFactory>();
}
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
protected override void AfterAddApplication(IServiceCollection services)
{
services.AddTransient<TestAbpClaimsPrincipalContributor>();
services.AddTransient<Test2AbpClaimsPrincipalContributor>();
services.AddTransient<Test3AbpClaimsPrincipalContributor>();
}
[Fact]
public async Task CreateAsync()
{
var claimsPrincipal = await _abpClaimsPrincipalFactory.CreateAsync();
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Email && x.Value == "admin2@abp.io");
claimsPrincipal.Claims.ShouldNotContain(x => x.Type == ClaimTypes.Email && x.Value == "admin@abp.io");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Version && x.Value == "2.0");
}
[Fact]
public async Task Create_With_Exists_ClaimsPrincipal()
{
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(TestAuthenticationType, ClaimTypes.Name, ClaimTypes.Role));
claimsPrincipal.Identities.First().AddClaim(new Claim(ClaimTypes.Name, "123"));
claimsPrincipal.Identities.First().AddClaim(new Claim(ClaimTypes.Role, "admin"));
await _abpClaimsPrincipalFactory.CreateAsync(claimsPrincipal);
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Name && x.Value == "123");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "admin");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Email && x.Value == "admin2@abp.io");
claimsPrincipal.Claims.ShouldNotContain(x => x.Type == ClaimTypes.Email && x.Value == "admin@abp.io");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Version && x.Value == "2.0");
}
class TestAbpClaimsPrincipalContributor : IAbpClaimsPrincipalContributor
{
public Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var claimsIdentity = context.ClaimsPrincipal.Identities.FirstOrDefault(x => x.AuthenticationType == TestAuthenticationType)
?? new ClaimsIdentity(TestAuthenticationType);
claimsIdentity.AddOrReplace(new Claim(ClaimTypes.Email, "admin@abp.io"));
context.ClaimsPrincipal.AddIdentityIfNotContains(claimsIdentity);
return Task.CompletedTask;
}
}
class Test2AbpClaimsPrincipalContributor : IAbpClaimsPrincipalContributor
{
public Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var claimsIdentity = context.ClaimsPrincipal.Identities.FirstOrDefault(x => x.AuthenticationType == TestAuthenticationType)
?? new ClaimsIdentity(TestAuthenticationType);
claimsIdentity.AddOrReplace(new Claim(ClaimTypes.Email, "admin2@abp.io"));
context.ClaimsPrincipal.AddIdentityIfNotContains(claimsIdentity);
return Task.CompletedTask;
}
}
class Test3AbpClaimsPrincipalContributor : IAbpClaimsPrincipalContributor
{
public Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var claimsIdentity = context.ClaimsPrincipal.Identities.FirstOrDefault(x => x.AuthenticationType == TestAuthenticationType)
?? new ClaimsIdentity(TestAuthenticationType);
claimsIdentity.AddOrReplace(new Claim(ClaimTypes.Version, "2.0"));
context.ClaimsPrincipal.AddIdentityIfNotContains(claimsIdentity);
return Task.CompletedTask;
}
}
}

189
framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/AbpClaimsPrincipalFactory_Tests.cs

@ -0,0 +1,189 @@
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Testing;
using Xunit;
namespace Volo.Abp.Security.Claims;
public class AbpClaimsPrincipalFactory_Tests : AbpIntegratedTest<AbpSecurityTestModule>
{
private readonly IAbpClaimsPrincipalFactory _abpClaimsPrincipalFactory;
private static string TestAuthenticationType => "Identity.Application";
public AbpClaimsPrincipalFactory_Tests()
{
_abpClaimsPrincipalFactory = GetRequiredService<IAbpClaimsPrincipalFactory>();
}
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
[Fact]
public async Task CreateAsync()
{
var claimsPrincipal = await _abpClaimsPrincipalFactory.CreateAsync();
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Email && x.Value == "admin2@abp.io");
claimsPrincipal.Claims.ShouldNotContain(x => x.Type == ClaimTypes.Email && x.Value == "admin@abp.io");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Version && x.Value == "2.0");
}
[Fact]
public async Task Create_With_Exists_ClaimsPrincipal()
{
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(TestAuthenticationType, ClaimTypes.Name, ClaimTypes.Role));
claimsPrincipal.Identities.First().AddClaim(new Claim(ClaimTypes.Name, "123"));
claimsPrincipal.Identities.First().AddClaim(new Claim(ClaimTypes.Role, "admin"));
await _abpClaimsPrincipalFactory.CreateAsync(claimsPrincipal);
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Name && x.Value == "123");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "admin");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Email && x.Value == "admin2@abp.io");
claimsPrincipal.Claims.ShouldNotContain(x => x.Type == ClaimTypes.Email && x.Value == "admin@abp.io");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Version && x.Value == "2.0");
}
[Fact]
public async Task DynamicCreateAsync()
{
var claimsPrincipal = await _abpClaimsPrincipalFactory.CreateDynamicAsync();
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Name && x.Value == "admin");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "admin");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Email && x.Value == "admin2@abp.io");
claimsPrincipal.Claims.ShouldNotContain(x => x.Type == ClaimTypes.Email && x.Value == "admin@abp.io");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Version && x.Value == "2.0");
}
[Fact]
public async Task DynamicCreate_With_Exists_ClaimsPrincipal()
{
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(TestAuthenticationType, ClaimTypes.Name, ClaimTypes.Role));
claimsPrincipal.Identities.First().AddClaim(new Claim(ClaimTypes.Name, "123"));
claimsPrincipal.Identities.First().AddClaim(new Claim(ClaimTypes.Role, "123"));
await _abpClaimsPrincipalFactory.CreateDynamicAsync(claimsPrincipal);
claimsPrincipal.Claims.ShouldNotContain(x => x.Type == ClaimTypes.Name && x.Value == "123");
claimsPrincipal.Claims.ShouldNotContain(x => x.Type == ClaimTypes.Role && x.Value == "123");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Name && x.Value == "admin");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "admin");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Email && x.Value == "admin2@abp.io");
claimsPrincipal.Claims.ShouldNotContain(x => x.Type == ClaimTypes.Email && x.Value == "admin@abp.io");
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Version && x.Value == "2.0");
}
class TestAbpClaimsPrincipalContributor : IAbpClaimsPrincipalContributor, ITransientDependency
{
public Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var claimsIdentity = context.ClaimsPrincipal.Identities.FirstOrDefault(x => x.AuthenticationType == TestAuthenticationType)
?? new ClaimsIdentity(TestAuthenticationType);
claimsIdentity.AddOrReplace(new Claim(ClaimTypes.Email, "admin@abp.io"));
context.ClaimsPrincipal.AddIdentityIfNotContains(claimsIdentity);
return Task.CompletedTask;
}
}
class Test2AbpClaimsPrincipalContributor : IAbpClaimsPrincipalContributor, ITransientDependency
{
public Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var claimsIdentity = context.ClaimsPrincipal.Identities.FirstOrDefault(x => x.AuthenticationType == TestAuthenticationType)
?? new ClaimsIdentity(TestAuthenticationType);
claimsIdentity.AddOrReplace(new Claim(ClaimTypes.Email, "admin2@abp.io"));
context.ClaimsPrincipal.AddIdentityIfNotContains(claimsIdentity);
return Task.CompletedTask;
}
}
class Test3AbpClaimsPrincipalContributor : IAbpClaimsPrincipalContributor, ITransientDependency
{
public Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var claimsIdentity = context.ClaimsPrincipal.Identities.FirstOrDefault(x => x.AuthenticationType == TestAuthenticationType)
?? new ClaimsIdentity(TestAuthenticationType);
claimsIdentity.AddOrReplace(new Claim(ClaimTypes.Version, "2.0"));
context.ClaimsPrincipal.AddIdentityIfNotContains(claimsIdentity);
return Task.CompletedTask;
}
}
class TestAbpDynamicClaimsPrincipalContributor : IAbpDynamicClaimsPrincipalContributor, ITransientDependency
{
public Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var claimsIdentity = context.ClaimsPrincipal.Identities.FirstOrDefault(x => x.AuthenticationType == TestAuthenticationType)
?? new ClaimsIdentity(TestAuthenticationType);
claimsIdentity.AddOrReplace(new Claim(ClaimTypes.Email, "admin@abp.io"));
context.ClaimsPrincipal.AddIdentityIfNotContains(claimsIdentity);
return Task.CompletedTask;
}
}
class Test2AbpDynamicClaimsPrincipalContributor : IAbpDynamicClaimsPrincipalContributor, ITransientDependency
{
public Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var claimsIdentity = context.ClaimsPrincipal.Identities.FirstOrDefault(x => x.AuthenticationType == TestAuthenticationType)
?? new ClaimsIdentity(TestAuthenticationType);
claimsIdentity.AddOrReplace(new Claim(ClaimTypes.Email, "admin2@abp.io"));
context.ClaimsPrincipal.AddIdentityIfNotContains(claimsIdentity);
return Task.CompletedTask;
}
}
class Test3AbpDynamicClaimsPrincipalContributor : IAbpDynamicClaimsPrincipalContributor, ITransientDependency
{
public Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var claimsIdentity = context.ClaimsPrincipal.Identities.FirstOrDefault(x => x.AuthenticationType == TestAuthenticationType)
?? new ClaimsIdentity(TestAuthenticationType);
claimsIdentity.AddOrReplace(new Claim(ClaimTypes.Version, "2.0"));
context.ClaimsPrincipal.AddIdentityIfNotContains(claimsIdentity);
return Task.CompletedTask;
}
}
class Test4AbpDynamicClaimsPrincipalContributor : IAbpDynamicClaimsPrincipalContributor, ITransientDependency
{
public Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var claimsIdentity = context.ClaimsPrincipal.Identities.FirstOrDefault(x => x.AuthenticationType == TestAuthenticationType)
?? new ClaimsIdentity(TestAuthenticationType);
claimsIdentity.AddOrReplace(new Claim(ClaimTypes.Name, "admin"));
claimsIdentity.RemoveAll(ClaimTypes.Role);
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "manager"));
context.ClaimsPrincipal.AddIdentityIfNotContains(claimsIdentity);
return Task.CompletedTask;
}
}
}

76
framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/AbpDynamicClaimsPrincipalContributorBase_Tests.cs

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Testing;
using Xunit;
namespace Volo.Abp.Security.Claims;
class TestAbpDynamicClaimsPrincipalContributor : AbpDynamicClaimsPrincipalContributorBase
{
public async override Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var identity = context.ClaimsPrincipal.Identities.FirstOrDefault();
Check.NotNull(identity, nameof(identity));
await AddDynamicClaimsAsync(context, identity, AbpDynamicClaimsPrincipalContributorBase_Tests.DynamicClaims.Claims);
}
}
public class AbpDynamicClaimsPrincipalContributorBase_Tests : AbpIntegratedTest<AbpSecurityTestModule>
{
private readonly TestAbpDynamicClaimsPrincipalContributor _dynamicClaimsPrincipalContributorBase = new TestAbpDynamicClaimsPrincipalContributor();
public readonly static AbpDynamicClaimCacheItem DynamicClaims = new AbpDynamicClaimCacheItem();
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
[Fact]
public async Task AddDynamicClaimsAsync()
{
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity());
claimsPrincipal.Identities.First().AddClaim(new Claim(AbpClaimTypes.UserName, "test-source-userName"));
claimsPrincipal.Identities.First().AddClaim(new Claim(AbpClaimTypes.Name, "test-source-name"));
claimsPrincipal.Identities.First().AddClaim(new Claim(AbpClaimTypes.SurName, "test-source-surname"));
claimsPrincipal.Identities.First().AddClaim(new Claim(AbpClaimTypes.Role, "test-source-role1"));
claimsPrincipal.Identities.First().AddClaim(new Claim(AbpClaimTypes.Role, "test-source-role2"));
claimsPrincipal.Identities.First().AddClaim(new Claim(AbpClaimTypes.Email, "test-source-email"));
claimsPrincipal.Identities.First().AddClaim(new Claim(AbpClaimTypes.EmailVerified, "test-source-emailVerified"));
claimsPrincipal.Identities.First().AddClaim(new Claim(AbpClaimTypes.PhoneNumber, "test-source-phoneNumber"));
claimsPrincipal.Identities.First().AddClaim(new Claim(AbpClaimTypes.PhoneNumberVerified, "test-source-phoneNumberVerified"));
claimsPrincipal.Identities.First().AddClaim(new Claim("my-claim", "test-source-my-claim"));
DynamicClaims.Claims.AddRange(new []
{
new AbpDynamicClaim("preferred_username", "test-preferred_username"),
new AbpDynamicClaim(ClaimTypes.GivenName, "test-given_name"),
new AbpDynamicClaim("family_name", "test-family_name"),
new AbpDynamicClaim("role", "test-role1"),
new AbpDynamicClaim("roles", "test-role2"),
new AbpDynamicClaim(ClaimTypes.Role, "test-role3"),
new AbpDynamicClaim("email", "test-email"),
new AbpDynamicClaim(AbpClaimTypes.EmailVerified, "test-email-verified"),
new AbpDynamicClaim(AbpClaimTypes.PhoneNumberVerified, null),
});
await _dynamicClaimsPrincipalContributorBase.ContributeAsync(new AbpClaimsPrincipalContributorContext(claimsPrincipal, GetRequiredService<IServiceProvider>()));
claimsPrincipal.Identities.First().Claims.ShouldContain(c => c.Type == AbpClaimTypes.UserName && c.Value == "test-preferred_username");
claimsPrincipal.Identities.First().Claims.ShouldContain(c => c.Type == AbpClaimTypes.SurName && c.Value == "test-family_name");
claimsPrincipal.Identities.First().Claims.ShouldContain(c => c.Type == AbpClaimTypes.Name && c.Value == "test-given_name");
claimsPrincipal.Identities.First().Claims.ShouldContain(c => c.Type == AbpClaimTypes.Role && c.Value == "test-role1");
claimsPrincipal.Identities.First().Claims.ShouldContain(c => c.Type == AbpClaimTypes.Role && c.Value == "test-role2");
claimsPrincipal.Identities.First().Claims.ShouldContain(c => c.Type == AbpClaimTypes.Role && c.Value == "test-role3");
claimsPrincipal.Identities.First().Claims.ShouldContain(c => c.Type == AbpClaimTypes.Email && c.Value == "test-email");
claimsPrincipal.Identities.First().Claims.ShouldContain(c => c.Type == AbpClaimTypes.EmailVerified && c.Value == "test-email-verified");
claimsPrincipal.Identities.First().Claims.ShouldContain(c => c.Type == AbpClaimTypes.PhoneNumber && c.Value == "test-source-phoneNumber");
claimsPrincipal.Identities.First().Claims.ShouldNotContain(c => c.Type == AbpClaimTypes.PhoneNumberVerified);
claimsPrincipal.Identities.First().Claims.ShouldContain(c => c.Type == "my-claim" && c.Value == "test-source-my-claim");
}
}

4
framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/CurrentPrincipalAccessor_Test.cs → framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/CurrentPrincipalAccessor_Tests.cs

@ -6,11 +6,11 @@ using Xunit;
namespace Volo.Abp.Security.Claims;
public class CurrentPrincipalAccessor_Test : AbpIntegratedTest<AbpSecurityTestModule>
public class CurrentPrincipalAccessor_Tests : AbpIntegratedTest<AbpSecurityTestModule>
{
private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor;
public CurrentPrincipalAccessor_Test()
public CurrentPrincipalAccessor_Tests()
{
_currentPrincipalAccessor = GetRequiredService<ICurrentPrincipalAccessor>();
}

10
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/IDynamicClaimsAppService.cs

@ -0,0 +1,10 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace Volo.Abp.Account;
public interface IDynamicClaimsAppService : IApplicationService
{
Task RefreshAsync();
}

31
modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/DynamicClaimsAppService.cs

@ -0,0 +1,31 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Identity;
using Volo.Abp.Security.Claims;
using Volo.Abp.Users;
namespace Volo.Abp.Account;
[Authorize]
public class DynamicClaimsAppService : IdentityAppServiceBase, IDynamicClaimsAppService
{
protected IdentityDynamicClaimsPrincipalContributorCache IdentityDynamicClaimsPrincipalContributorCache { get; }
protected IAbpClaimsPrincipalFactory AbpClaimsPrincipalFactory { get; }
protected ICurrentPrincipalAccessor PrincipalAccessor { get; }
public DynamicClaimsAppService(
IdentityDynamicClaimsPrincipalContributorCache identityDynamicClaimsPrincipalContributorCache,
IAbpClaimsPrincipalFactory abpClaimsPrincipalFactory,
ICurrentPrincipalAccessor principalAccessor)
{
IdentityDynamicClaimsPrincipalContributorCache = identityDynamicClaimsPrincipalContributorCache;
AbpClaimsPrincipalFactory = abpClaimsPrincipalFactory;
PrincipalAccessor = principalAccessor;
}
public virtual async Task RefreshAsync()
{
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(CurrentUser.GetId(), CurrentUser.TenantId);
await AbpClaimsPrincipalFactory.CreateDynamicAsync(PrincipalAccessor.Principal);
}
}

24
modules/account/src/Volo.Abp.Account.HttpApi.Client/ClientProxies/Volo/Abp/Account/DynamicClaimsClientProxy.Generated.cs

@ -0,0 +1,24 @@
// This file is automatically generated by ABP framework to use MVC Controllers from CSharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Account;
using Volo.Abp.Application.Dtos;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.ClientProxying;
using Volo.Abp.Http.Modeling;
// ReSharper disable once CheckNamespace
namespace Volo.Abp.Account;
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IDynamicClaimsAppService), typeof(DynamicClaimsClientProxy))]
public partial class DynamicClaimsClientProxy : ClientProxyBase<IDynamicClaimsAppService>, IDynamicClaimsAppService
{
public virtual async Task RefreshAsync()
{
await RequestAsync(nameof(RefreshAsync));
}
}

7
modules/account/src/Volo.Abp.Account.HttpApi.Client/ClientProxies/Volo/Abp/Account/DynamicClaimsClientProxy.cs

@ -0,0 +1,7 @@
// This file is part of DynamicClaimsClientProxy, you can customize it here
// ReSharper disable once CheckNamespace
namespace Volo.Abp.Account;
public partial class DynamicClaimsClientProxy
{
}

41
modules/account/src/Volo.Abp.Account.HttpApi.Client/ClientProxies/account-generate-proxy.json

@ -338,6 +338,47 @@
}
}
},
"Volo.Abp.Account.DynamicClaimsController": {
"controllerName": "DynamicClaims",
"controllerGroupName": "DynamicClaims",
"isRemoteService": true,
"isIntegrationService": false,
"apiVersion": null,
"type": "Volo.Abp.Account.DynamicClaimsController",
"interfaces": [
{
"type": "Volo.Abp.Account.IDynamicClaimsAppService",
"name": "IDynamicClaimsAppService",
"methods": [
{
"name": "RefreshAsync",
"parametersOnMethod": [],
"returnValue": {
"type": "System.Void",
"typeSimple": "System.Void"
}
}
]
}
],
"actions": {
"RefreshAsync": {
"uniqueName": "RefreshAsync",
"name": "RefreshAsync",
"httpMethod": "POST",
"url": "api/account/dynamic-claims/refresh",
"supportedVersions": [],
"parametersOnMethod": [],
"parameters": [],
"returnValue": {
"type": "System.Void",
"typeSimple": "System.Void"
},
"allowAnonymous": null,
"implementFrom": "Volo.Abp.Account.IDynamicClaimsAppService"
}
}
},
"Volo.Abp.Account.ProfileController": {
"controllerName": "Profile",
"controllerGroupName": "Profile",

27
modules/account/src/Volo.Abp.Account.HttpApi/Volo/Abp/Account/DynamicClaimsController.cs

@ -0,0 +1,27 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace Volo.Abp.Account;
[RemoteService(Name = AccountRemoteServiceConsts.RemoteServiceName)]
[Area(AccountRemoteServiceConsts.ModuleName)]
[ControllerName("DynamicClaims")]
[Route("/api/account/dynamic-claims")]
public class DynamicClaimsController : AbpControllerBase, IDynamicClaimsAppService
{
protected IDynamicClaimsAppService DynamicClaimsAppService { get; }
public DynamicClaimsController(IDynamicClaimsAppService dynamicClaimsAppService)
{
DynamicClaimsAppService = dynamicClaimsAppService;
}
[HttpPost]
[Route("refresh")]
public virtual Task RefreshAsync()
{
return DynamicClaimsAppService.RefreshAsync();
}
}

14
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs

@ -10,7 +10,6 @@ using System;
using System.Diagnostics;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Volo.Abp.Account.Settings;
@ -32,14 +31,12 @@ public class IdentityServerSupportedLoginModel : LoginModel
public IdentityServerSupportedLoginModel(
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions,
IOptions<IdentityOptions> identityOptions,
IdentityDynamicClaimsPrincipalContributorCache identityDynamicClaimsPrincipalContributorCache,
IIdentityServerInteractionService interaction,
IClientStore clientStore,
IEventService identityServerEvents,
IOptions<IdentityOptions> identityOptions)
: base(
schemeProvider,
accountOptions,
identityOptions)
IEventService identityServerEvents)
: base(schemeProvider, accountOptions, identityOptions, identityDynamicClaimsPrincipalContributorCache)
{
Interaction = interaction;
ClientStore = clientStore;
@ -177,6 +174,9 @@ public class IdentityServerSupportedLoginModel : LoginModel
Debug.Assert(user != null, nameof(user) + " != null");
await IdentityServerEvents.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id.ToString(), user.UserName)); //TODO: Use user's name once implemented
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
return RedirectSafely(ReturnUrl, ReturnUrlHash);
}

5
modules/account/src/Volo.Abp.Account.Web.OpenIddict/Pages/Account/OpenIddictSupportedLoginModel.cs

@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Mvc;
using OpenIddict.Server;
using OpenIddict.Server.AspNetCore;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.OpenIddict;
@ -17,12 +18,14 @@ namespace Volo.Abp.Account.Web.Pages.Account;
public class OpenIddictSupportedLoginModel : LoginModel
{
protected AbpOpenIddictRequestHelper OpenIddictRequestHelper { get; }
public OpenIddictSupportedLoginModel(
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions,
IOptions<IdentityOptions> identityOptions,
IdentityDynamicClaimsPrincipalContributorCache identityDynamicClaimsPrincipalContributorCache,
AbpOpenIddictRequestHelper openIddictRequestHelper)
: base(schemeProvider, accountOptions, identityOptions)
: base(schemeProvider, accountOptions, identityOptions, identityDynamicClaimsPrincipalContributorCache)
{
OpenIddictRequestHelper = openIddictRequestHelper;
}

15
modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs

@ -29,13 +29,15 @@ public class AccountController : AbpControllerBase
protected ISettingProvider SettingProvider { get; }
protected IdentitySecurityLogManager IdentitySecurityLogManager { get; }
protected IOptions<IdentityOptions> IdentityOptions { get; }
protected IdentityDynamicClaimsPrincipalContributorCache IdentityDynamicClaimsPrincipalContributorCache { get; }
public AccountController(
SignInManager<IdentityUser> signInManager,
IdentityUserManager userManager,
ISettingProvider settingProvider,
IdentitySecurityLogManager identitySecurityLogManager,
IOptions<IdentityOptions> identityOptions)
IOptions<IdentityOptions> identityOptions,
IdentityDynamicClaimsPrincipalContributorCache identityDynamicClaimsPrincipalContributorCache)
{
LocalizationResource = typeof(AccountResource);
@ -44,6 +46,7 @@ public class AccountController : AbpControllerBase
SettingProvider = settingProvider;
IdentitySecurityLogManager = identitySecurityLogManager;
IdentityOptions = identityOptions;
IdentityDynamicClaimsPrincipalContributorCache = identityDynamicClaimsPrincipalContributorCache;
}
[HttpPost]
@ -69,6 +72,16 @@ public class AccountController : AbpControllerBase
UserName = login.UserNameOrEmailAddress
});
if (signInResult.Succeeded)
{
var user = await UserManager.FindByNameAsync(login.UserNameOrEmailAddress);
if (user != null)
{
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
}
}
return GetAbpLoginResult(signInResult);
}

22
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs

@ -52,17 +52,19 @@ public class LoginModel : AccountPageModel
protected IAuthenticationSchemeProvider SchemeProvider { get; }
protected AbpAccountOptions AccountOptions { get; }
protected IOptions<IdentityOptions> IdentityOptions { get; }
protected IdentityDynamicClaimsPrincipalContributorCache IdentityDynamicClaimsPrincipalContributorCache { get; }
public bool ShowCancelButton { get; set; }
public LoginModel(
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions,
IOptions<IdentityOptions> identityOptions)
IOptions<IdentityOptions> identityOptions,
IdentityDynamicClaimsPrincipalContributorCache identityDynamicClaimsPrincipalContributorCache)
{
SchemeProvider = schemeProvider;
IdentityOptions = identityOptions;
AccountOptions = accountOptions.Value;
IdentityDynamicClaimsPrincipalContributorCache = identityDynamicClaimsPrincipalContributorCache;
}
public virtual async Task<IActionResult> OnGetAsync()
@ -138,6 +140,9 @@ public class LoginModel : AccountPageModel
Debug.Assert(user != null, nameof(user) + " != null");
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
return RedirectSafely(ReturnUrl, ReturnUrlHash);
}
@ -222,8 +227,16 @@ public class LoginModel : AccountPageModel
throw new UserFriendlyException("Cannot proceed because user is not allowed!");
}
IdentityUser user;
if (result.Succeeded)
{
user = await UserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey);
if (user != null)
{
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
}
return RedirectSafely(returnUrl, returnUrlHash);
}
@ -239,7 +252,7 @@ public class LoginModel : AccountPageModel
});
}
var user = await UserManager.FindByEmailAsync(email);
user = await UserManager.FindByEmailAsync(email);
if (user == null)
{
return RedirectToPage("./Register", new {
@ -263,6 +276,9 @@ public class LoginModel : AccountPageModel
UserName = user.Name
});
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
return RedirectSafely(returnUrl, returnUrlHash);
}

11
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs

@ -46,13 +46,16 @@ public class RegisterModel : AccountPageModel
protected IAuthenticationSchemeProvider SchemeProvider { get; }
protected AbpAccountOptions AccountOptions { get; }
protected IdentityDynamicClaimsPrincipalContributorCache IdentityDynamicClaimsPrincipalContributorCache { get; }
public RegisterModel(
IAccountAppService accountAppService,
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions)
IOptions<AbpAccountOptions> accountOptions,
IdentityDynamicClaimsPrincipalContributorCache identityDynamicClaimsPrincipalContributorCache)
{
SchemeProvider = schemeProvider;
IdentityDynamicClaimsPrincipalContributorCache = identityDynamicClaimsPrincipalContributorCache;
AccountAppService = accountAppService;
AccountOptions = accountOptions.Value;
}
@ -159,6 +162,9 @@ public class RegisterModel : AccountPageModel
var user = await UserManager.GetByIdAsync(userDto.Id);
await SignInManager.SignInAsync(user, isPersistent: true);
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
}
protected virtual async Task RegisterExternalUserAsync(ExternalLoginInfo externalLoginInfo, string userName, string emailAddress)
@ -185,6 +191,9 @@ public class RegisterModel : AccountPageModel
}
await SignInManager.SignInAsync(user, isPersistent: true, ExternalLoginAuthSchema);
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
}
protected virtual async Task<bool> CheckSelfRegistrationAsync()

15
modules/account/src/Volo.Abp.Account.Web/wwwroot/client-proxies/account-proxy.js

@ -79,6 +79,21 @@
})();
// controller volo.abp.account.dynamicClaims
(function(){
abp.utils.createNamespace(window, 'volo.abp.account.dynamicClaims');
volo.abp.account.dynamicClaims.get = function(ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/account/dynamic-claims',
type: 'GET'
}, ajaxParams));
};
})();
// controller volo.abp.account.profile
(function(){

2
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpUserClaimsPrincipalFactory.cs

@ -33,7 +33,7 @@ public class AbpUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<Identity
}
[UnitOfWork]
public override async Task<ClaimsPrincipal> CreateAsync(IdentityUser user)
public async override Task<ClaimsPrincipal> CreateAsync(IdentityUser user)
{
var principal = await base.CreateAsync(user);
var identity = principal.Identities.First();

39
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributor.cs

@ -0,0 +1,39 @@
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.Identity;
public class IdentityDynamicClaimsPrincipalContributor : AbpDynamicClaimsPrincipalContributorBase
{
public async override Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var identity = context.ClaimsPrincipal.Identities.FirstOrDefault();
var userId = identity?.FindUserId();
if (userId == null)
{
return;
}
var dynamicClaimsCache = context.GetRequiredService<IdentityDynamicClaimsPrincipalContributorCache>();
AbpDynamicClaimCacheItem dynamicClaims;
try
{
dynamicClaims = await dynamicClaimsCache.GetAsync(userId.Value, identity.FindTenantId());
}
catch (EntityNotFoundException e)
{
// In case if user not found, We force to clear the claims principal.
context.ClaimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity());
var logger = context.GetRequiredService<ILogger<IdentityDynamicClaimsPrincipalContributor>>();
logger.LogWarning(e, $"User not found: {userId.Value}");
return;
}
await AddDynamicClaimsAsync(context, identity, dynamicClaims.Claims);
}
}

86
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributorCache.cs

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.Identity;
public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependency
{
public ILogger<IdentityDynamicClaimsPrincipalContributorCache> Logger { get; set; }
protected IDistributedCache<AbpDynamicClaimCacheItem> Cache { get; }
protected ICurrentTenant CurrentTenant { get; }
protected IdentityUserManager UserManager { get; }
protected IUserClaimsPrincipalFactory<IdentityUser> UserClaimsPrincipalFactory { get; }
protected IOptions<AbpClaimsPrincipalFactoryOptions> AbpClaimsPrincipalFactoryOptions { get; }
protected IOptions<IdentityDynamicClaimsPrincipalContributorCacheOptions> CacheOptions { get; }
public IdentityDynamicClaimsPrincipalContributorCache(
IDistributedCache<AbpDynamicClaimCacheItem> cache,
ICurrentTenant currentTenant,
IdentityUserManager userManager,
IUserClaimsPrincipalFactory<IdentityUser> userClaimsPrincipalFactory,
IOptions<AbpClaimsPrincipalFactoryOptions> abpClaimsPrincipalFactoryOptions,
IOptions<IdentityDynamicClaimsPrincipalContributorCacheOptions> cacheOptions)
{
Cache = cache;
CurrentTenant = currentTenant;
UserManager = userManager;
UserClaimsPrincipalFactory = userClaimsPrincipalFactory;
AbpClaimsPrincipalFactoryOptions = abpClaimsPrincipalFactoryOptions;
CacheOptions = cacheOptions;
Logger = NullLogger<IdentityDynamicClaimsPrincipalContributorCache>.Instance;
}
public virtual async Task<AbpDynamicClaimCacheItem> GetAsync(Guid userId, Guid? tenantId = null)
{
Logger.LogDebug($"Get dynamic claims cache for user: {userId}");
return await Cache.GetOrAddAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId), async () =>
{
using (CurrentTenant.Change(tenantId))
{
Logger.LogDebug($"Filling dynamic claims cache for user: {userId}");
var user = await UserManager.GetByIdAsync(userId);
var principal = await UserClaimsPrincipalFactory.CreateAsync(user);
var dynamicClaims = new AbpDynamicClaimCacheItem();
foreach (var claimType in AbpClaimsPrincipalFactoryOptions.Value.DynamicClaims)
{
var claims = principal.Claims.Where(x => x.Type == claimType).ToList();
if (claims.Any())
{
dynamicClaims.Claims.AddRange(claims.Select(claim => new AbpDynamicClaim(claimType, claim.Value)));
}
else
{
dynamicClaims.Claims.Add(new AbpDynamicClaim(claimType, null));
}
}
return dynamicClaims;
}
}, () => new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = CacheOptions.Value.CacheAbsoluteExpiration
});
}
public virtual async Task ClearAsync(Guid userId, Guid? tenantId = null)
{
Logger.LogDebug($"Clearing dynamic claims cache for user: {userId}");
await Cache.RemoveAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId));
}
}

13
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributorCacheOptions.cs

@ -0,0 +1,13 @@
using System;
namespace Volo.Abp.Identity;
public class IdentityDynamicClaimsPrincipalContributorCacheOptions
{
public TimeSpan CacheAbsoluteExpiration { get; set; }
public IdentityDynamicClaimsPrincipalContributorCacheOptions()
{
CacheAbsoluteExpiration = TimeSpan.FromHours(1);
}
}

40
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserUpdatedEventHandler.cs

@ -0,0 +1,40 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
using Volo.Abp.Uow;
namespace Volo.Abp.Identity;
public class UserEntityUpdatedEventHandler :
ILocalEventHandler<EntityUpdatedEventData<IdentityUser>>,
ILocalEventHandler<EntityDeletedEventData<IdentityUser>>,
ITransientDependency
{
private readonly IdentityDynamicClaimsPrincipalContributorCache _cache;
public UserEntityUpdatedEventHandler(IdentityDynamicClaimsPrincipalContributorCache cache)
{
_cache = cache;
}
[UnitOfWork]
public virtual async Task HandleEventAsync(EntityUpdatedEventData<IdentityUser> eventData)
{
await ClearAsync(eventData.Entity.Id, eventData.Entity.TenantId);
}
[UnitOfWork]
public virtual async Task HandleEventAsync(EntityDeletedEventData<IdentityUser> eventData)
{
await ClearAsync(eventData.Entity.Id, eventData.Entity.TenantId);
}
protected virtual async Task ClearAsync(Guid userId, Guid? tenantId)
{
await _cache.ClearAsync(userId, tenantId);
}
}

8
modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpUserClaimsPrincipalFactory_Tests.cs

@ -2,6 +2,7 @@
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
@ -22,6 +23,11 @@ public class AbpUserClaimsPrincipalFactory_Tests : AbpIdentityDomainTestBase
_testData = GetRequiredService<IdentityTestData>();
}
protected override void AfterAddApplication(IServiceCollection services)
{
services.AddTransient<TestAbpClaimsPrincipalContributor>();
}
[Fact]
public async Task Add_And_Replace_Claims_Test()
{
@ -42,7 +48,7 @@ public class AbpUserClaimsPrincipalFactory_Tests : AbpIdentityDomainTestBase
});
}
class TestAbpClaimsPrincipalContributor : IAbpClaimsPrincipalContributor, ITransientDependency
class TestAbpClaimsPrincipalContributor : IAbpClaimsPrincipalContributor
{
//https://github.com/dotnet/aspnetcore/blob/v5.0.0/src/Identity/Extensions.Core/src/UserClaimsPrincipalFactory.cs#L79
private static string IdentityAuthenticationType => "Identity.Application";

59
modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributor_Tests.cs

@ -0,0 +1,59 @@
using System.Security.Claims;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Security.Claims;
using Xunit;
namespace Volo.Abp.Identity;
public class IdentityDynamicClaimsPrincipalContributor_Tests : AbpIdentityDomainTestBase
{
private readonly IdentityUserManager _identityUserManager;
private readonly IAbpClaimsPrincipalFactory _abpClaimsPrincipalFactory;
private readonly AbpUserClaimsPrincipalFactory _abpUserClaimsPrincipalFactory;
private readonly IdentityTestData _testData;
public IdentityDynamicClaimsPrincipalContributor_Tests()
{
_identityUserManager = GetRequiredService<IdentityUserManager>();
_abpClaimsPrincipalFactory = GetRequiredService<IAbpClaimsPrincipalFactory>();
_abpUserClaimsPrincipalFactory = GetRequiredService<AbpUserClaimsPrincipalFactory>();
_testData = GetRequiredService<IdentityTestData>();
}
[Fact]
public async Task Should_Get_Correct_Claims_After_User_Updating()
{
IdentityUser user = null;
ClaimsPrincipal claimsPrincipal = null;
string securityStamp = null;
await UsingUowAsync(async () =>
{
user = await _identityUserManager.GetByIdAsync(_testData.UserJohnId);
user.ShouldNotBeNull();
securityStamp = user.SecurityStamp;
claimsPrincipal = await _abpUserClaimsPrincipalFactory.CreateAsync(user);
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.NameIdentifier && x.Value == user.Id.ToString());
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Name && x.Value == user.UserName);
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Email && x.Value == user.Email);
claimsPrincipal.Claims.ShouldContain(x => x.Type == "AspNet.Identity.SecurityStamp" && x.Value == securityStamp);
var dynamicClaimsPrincipal = await _abpClaimsPrincipalFactory.CreateDynamicAsync(claimsPrincipal);
dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.NameIdentifier && x.Value == user.Id.ToString());
dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Name && x.Value == user.UserName);
dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Email && x.Value == user.Email);
dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == "AspNet.Identity.SecurityStamp" && x.Value == securityStamp);//SecurityStamp is not dynamic claim
await _identityUserManager.SetUserNameAsync(user, "newUserName");
await _identityUserManager.SetEmailAsync(user, "newUserEmail@abp.io");
await _identityUserManager.UpdateSecurityStampAsync(user);
});
var dynamicClaimsPrincipal = await _abpClaimsPrincipalFactory.CreateDynamicAsync(claimsPrincipal);
dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.NameIdentifier && x.Value == user.Id.ToString());
dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Name && x.Value =="newUserName");
dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Email && x.Value == "newUserEmail@abp.io");
dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == "AspNet.Identity.SecurityStamp" && x.Value == securityStamp);//SecurityStamp is not dynamic claim
}
}

1
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs

@ -24,6 +24,7 @@ public class AbpOpenIddictAspNetCoreModule : AbpModule
Configure<AbpOpenIddictClaimsPrincipalOptions>(options =>
{
options.ClaimsPrincipalHandlers.Add<AbpDynamicClaimsOpenIddictClaimsPrincipalHandler>();
options.ClaimsPrincipalHandlers.Add<AbpDefaultOpenIddictClaimsPrincipalHandler>();
});

18
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Claims/AbpDynamicClaimsOpenIddictClaimsPrincipalHandler.cs

@ -0,0 +1,18 @@
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.OpenIddict;
public class AbpDynamicClaimsOpenIddictClaimsPrincipalHandler: IAbpOpenIddictClaimsPrincipalHandler, ITransientDependency
{
public virtual async Task HandleAsync(AbpOpenIddictClaimsPrincipalHandlerContext context)
{
var abpClaimsPrincipalFactory = context
.ScopeServiceProvider
.GetRequiredService<IAbpClaimsPrincipalFactory>();
await abpClaimsPrincipalFactory.CreateDynamicAsync(context.Principal);
}
}

2
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AbpOpenIdDictControllerBase.cs

@ -13,6 +13,7 @@ using OpenIddict.Abstractions;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Identity;
using Volo.Abp.OpenIddict.Localization;
using Volo.Abp.Security.Claims;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
namespace Volo.Abp.OpenIddict.Controllers;
@ -26,6 +27,7 @@ public abstract class AbpOpenIdDictControllerBase : AbpController
protected IOpenIddictScopeManager ScopeManager => LazyServiceProvider.LazyGetRequiredService<IOpenIddictScopeManager>();
protected IOpenIddictTokenManager TokenManager => LazyServiceProvider.LazyGetRequiredService<IOpenIddictTokenManager>();
protected AbpOpenIddictClaimsPrincipalManager OpenIddictClaimsPrincipalManager => LazyServiceProvider.LazyGetRequiredService<AbpOpenIddictClaimsPrincipalManager>();
protected IAbpClaimsPrincipalFactory AbpClaimsPrincipalFactory => LazyServiceProvider.LazyGetRequiredService<IAbpClaimsPrincipalFactory>();
protected AbpOpenIdDictControllerBase()
{

14
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs

@ -78,8 +78,18 @@ public class AuthorizeController : AbpOpenIdDictControllerBase
}
// Retrieve the profile of the logged in user.
var user = await UserManager.GetUserAsync(result.Principal) ??
throw new InvalidOperationException(L["TheUserDetailsCannotBbeRetrieved"]);
var dynamicPrincipal = await AbpClaimsPrincipalFactory.CreateDynamicAsync(result.Principal);
var user = await UserManager.GetUserAsync(dynamicPrincipal);
if (user == null)
{
return Challenge(
authenticationSchemes: IdentityConstants.ApplicationScheme,
properties: new AuthenticationProperties
{
RedirectUri = Request.PathBase + Request.Path + QueryString.Create(
Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList())
});
}
// Retrieve the application details from the database.
var application = await ApplicationManager.FindByClientIdAsync(request.ClientId) ??

1
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.AuthorizationCode.cs

@ -14,6 +14,7 @@ public partial class TokenController
{
// Retrieve the claims principal stored in the authorization code/device code/refresh token.
var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal;
principal = await AbpClaimsPrincipalFactory.CreateDynamicAsync(principal);
using (CurrentTenant.Change(principal.FindTenantId()))
{
// Retrieve the user profile corresponding to the authorization code/refresh token.

1
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.DeviceCode.cs

@ -14,6 +14,7 @@ public partial class TokenController
{
// Retrieve the claims principal stored in the authorization code/device code/refresh token.
var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal;
principal = await AbpClaimsPrincipalFactory.CreateDynamicAsync(principal);
using (CurrentTenant.Change(principal.FindTenantId()))
{
// Retrieve the user profile corresponding to the authorization code/refresh token.

4
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs

@ -31,6 +31,7 @@ public partial class TokenController
protected IdentitySecurityLogManager IdentitySecurityLogManager => LazyServiceProvider.LazyGetRequiredService<IdentitySecurityLogManager>();
protected ISettingProvider SettingProvider => LazyServiceProvider.LazyGetRequiredService<ISettingProvider>();
protected IdentityDynamicClaimsPrincipalContributorCache IdentityDynamicClaimsPrincipalContributorCache => LazyServiceProvider.LazyGetRequiredService<IdentityDynamicClaimsPrincipalContributorCache>();
[UnitOfWork]
protected virtual async Task<IActionResult> HandlePasswordAsync(OpenIddictRequest request)
@ -334,6 +335,9 @@ public partial class TokenController
protected virtual async Task<IActionResult> SetSuccessResultAsync(OpenIddictRequest request, IdentityUser user)
{
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
// Create a new ClaimsPrincipal containing the claims that
// will be used to create an id_token, a token or a code.
var principal = await SignInManager.CreateUserPrincipalAsync(user);

1
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.RefreshToken.cs

@ -14,6 +14,7 @@ public partial class TokenController
{
// Retrieve the claims principal stored in the authorization code/device code/refresh token.
var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal;
principal = await AbpClaimsPrincipalFactory.CreateDynamicAsync(principal);
using (CurrentTenant.Change(principal.FindTenantId()))
{
// Retrieve the user profile corresponding to the authorization code/refresh token.

6
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameModule.cs

@ -47,6 +47,7 @@ using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.Blazor.Server;
using Volo.Abp.TenantManagement.MongoDB;
using Volo.Abp.OpenIddict;
using Volo.Abp.Security.Claims;
using Volo.Abp.UI.Navigation;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.Uow;
@ -174,6 +175,10 @@ public class MyProjectNameModule : AbpModule
private void ConfigureAuthentication(ServiceConfigurationContext context)
{
context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
});
}
private void ConfigureUrls(IConfiguration configuration)
@ -358,6 +363,7 @@ public class MyProjectNameModule : AbpModule
}
app.UseUnitOfWork();
app.UseDynamicClaims();
app.UseAuthorization();
app.UseSwagger();

6
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameModule.cs

@ -49,6 +49,7 @@ using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.Blazor.Server;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
using Volo.Abp.OpenIddict;
using Volo.Abp.Security.Claims;
using Volo.Abp.UI.Navigation;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.Validation.Localization;
@ -176,6 +177,10 @@ public class MyProjectNameModule : AbpModule
private void ConfigureAuthentication(ServiceConfigurationContext context)
{
context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
});
}
private void ConfigureUrls(IConfiguration configuration)
@ -374,6 +379,7 @@ public class MyProjectNameModule : AbpModule
}
app.UseUnitOfWork();
app.UseDynamicClaims();
app.UseAuthorization();
app.UseSwagger();

6
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/MyProjectNameHostModule.cs

@ -41,6 +41,7 @@ using Volo.Abp.Swashbuckle;
using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.MongoDB;
using Volo.Abp.OpenIddict;
using Volo.Abp.Security.Claims;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.Uow;
using Volo.Abp.VirtualFileSystem;
@ -160,6 +161,10 @@ public class MyProjectNameHostModule : AbpModule
private void ConfigureAuthentication(ServiceConfigurationContext context)
{
context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
});
}
private void ConfigureBundles()
@ -312,6 +317,7 @@ public class MyProjectNameHostModule : AbpModule
}
app.UseUnitOfWork();
app.UseDynamicClaims();
app.UseAuthorization();
app.UseSwagger();

6
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/MyProjectNameHostModule.cs

@ -43,6 +43,7 @@ using Volo.Abp.Swashbuckle;
using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
using Volo.Abp.OpenIddict;
using Volo.Abp.Security.Claims;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.Uow;
using Volo.Abp.VirtualFileSystem;
@ -163,6 +164,10 @@ public class MyProjectNameHostModule : AbpModule
private void ConfigureAuthentication(ServiceConfigurationContext context)
{
context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
});
}
private void ConfigureBundles()
@ -329,6 +334,7 @@ public class MyProjectNameHostModule : AbpModule
}
app.UseUnitOfWork();
app.UseDynamicClaims();
app.UseAuthorization();
app.UseSwagger();

6
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameModule.cs

@ -41,6 +41,7 @@ using Volo.Abp.Swashbuckle;
using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.MongoDB;
using Volo.Abp.OpenIddict;
using Volo.Abp.Security.Claims;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.Uow;
using Volo.Abp.Validation.Localization;
@ -162,6 +163,10 @@ public class MyProjectNameModule : AbpModule
private void ConfigureAuthentication(ServiceConfigurationContext context)
{
context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
});
}
private void ConfigureBundles()
@ -354,6 +359,7 @@ public class MyProjectNameModule : AbpModule
}
app.UseUnitOfWork();
app.UseDynamicClaims();
app.UseAuthorization();
app.UseSwagger();

6
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameModule.cs

@ -44,6 +44,7 @@ using Volo.Abp.Swashbuckle;
using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
using Volo.Abp.OpenIddict;
using Volo.Abp.Security.Claims;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.Validation.Localization;
using Volo.Abp.VirtualFileSystem;
@ -165,6 +166,10 @@ public class MyProjectNameModule : AbpModule
private void ConfigureAuthentication(ServiceConfigurationContext context)
{
context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
});
}
private void ConfigureBundles()
@ -371,6 +376,7 @@ public class MyProjectNameModule : AbpModule
}
app.UseUnitOfWork();
app.UseDynamicClaims();
app.UseAuthorization();
app.UseSwagger();

6
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameModule.cs

@ -43,6 +43,7 @@ using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.MongoDB;
using Volo.Abp.TenantManagement.Web;
using Volo.Abp.OpenIddict;
using Volo.Abp.Security.Claims;
using Volo.Abp.UI.Navigation;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.Uow;
@ -167,6 +168,10 @@ public class MyProjectNameModule : AbpModule
private void ConfigureAuthentication(ServiceConfigurationContext context)
{
context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
});
}
private void ConfigureMultiTenancy()
@ -335,6 +340,7 @@ public class MyProjectNameModule : AbpModule
}
app.UseUnitOfWork();
app.UseDynamicClaims();
app.UseAuthorization();
app.UseSwagger();

6
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameModule.cs

@ -45,6 +45,7 @@ using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
using Volo.Abp.TenantManagement.Web;
using Volo.Abp.OpenIddict;
using Volo.Abp.Security.Claims;
using Volo.Abp.UI.Navigation;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.Validation.Localization;
@ -169,6 +170,10 @@ public class MyProjectNameModule : AbpModule
private void ConfigureAuthentication(ServiceConfigurationContext context)
{
context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
});
}
private void ConfigureMultiTenancy()
@ -351,6 +356,7 @@ public class MyProjectNameModule : AbpModule
}
app.UseUnitOfWork();
app.UseDynamicClaims();
app.UseAuthorization();
app.UseSwagger();

7
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyProjectNameAuthServerModule.cs

@ -32,6 +32,7 @@ using Volo.Abp.DistributedLocking;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.OpenIddict;
using Volo.Abp.Security.Claims;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.UI;
using Volo.Abp.VirtualFileSystem;
@ -180,6 +181,11 @@ public class MyProjectNameAuthServerModule : AbpModule
.AllowCredentials();
});
});
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
@ -211,6 +217,7 @@ public class MyProjectNameAuthServerModule : AbpModule
app.UseMultiTenancy();
}
app.UseDynamicClaims();
app.UseUnitOfWork();
app.UseAuthorization();
app.UseAuditing();

11
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBlazorModule.cs

@ -44,6 +44,7 @@ using Volo.Abp.Identity.Blazor.Server;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Security.Claims;
using Volo.Abp.SettingManagement.Blazor.Server;
using Volo.Abp.Swashbuckle;
using Volo.Abp.TenantManagement.Blazor.Server;
@ -188,7 +189,7 @@ public class MyProjectNameBlazorModule : AbpModule
* This configuration is used when the AuthServer is running on the internal network such as docker or k8s.
* Configuring the redirecting URLs for internal network and the web
* The login and the logout URLs are configured to redirect to the AuthServer real DNS for browser.
* The token acquired and validated from the the internal network AuthServer URL.
* The token acquired and validated from the the internal network AuthServer URL.
*/
if (configuration.GetValue<bool>("AuthServer:IsContainerized"))
{
@ -227,6 +228,12 @@ public class MyProjectNameBlazorModule : AbpModule
};
});
}
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
options.RemoteRefreshUrl = configuration["AuthServer:Authority"] + options.RemoteRefreshUrl;
});
}
private void ConfigureVirtualFileSystem(IWebHostEnvironment hostingEnvironment)
@ -351,7 +358,7 @@ public class MyProjectNameBlazorModule : AbpModule
{
app.UseMultiTenancy();
}
app.UseDynamicClaims();
app.UseAuthorization();
app.UseSwagger();
app.UseAbpSwaggerUI(options =>

7
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBlazorModule.cs

@ -34,6 +34,7 @@ using Volo.Abp.AutoMapper;
using Volo.Abp.Identity.Blazor.Server;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Security.Claims;
using Volo.Abp.SettingManagement.Blazor.Server;
using Volo.Abp.Swashbuckle;
using Volo.Abp.TenantManagement.Blazor.Server;
@ -122,6 +123,10 @@ public class MyProjectNameBlazorModule : AbpModule
private void ConfigureAuthentication(ServiceConfigurationContext context)
{
context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
});
}
private void ConfigureUrls(IConfiguration configuration)
@ -265,7 +270,7 @@ public class MyProjectNameBlazorModule : AbpModule
{
app.UseMultiTenancy();
}
app.UseDynamicClaims();
app.UseUnitOfWork();
app.UseAuthorization();
app.UseSwagger();

7
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyProjectNameBlazorModule.cs

@ -15,6 +15,7 @@ using Volo.Abp.AutoMapper;
using Volo.Abp.Modularity;
using Volo.Abp.UI.Navigation;
using Volo.Abp.Identity.Blazor.WebAssembly;
using Volo.Abp.Security.Claims;
using Volo.Abp.SettingManagement.Blazor.WebAssembly;
using Volo.Abp.TenantManagement.Blazor.WebAssembly;
@ -80,6 +81,12 @@ public class MyProjectNameBlazorModule : AbpModule
options.ProviderOptions.DefaultScopes.Add("email");
options.ProviderOptions.DefaultScopes.Add("phone");
});
builder.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
options.RemoteRefreshUrl = builder.Configuration["AuthServer:Authority"] + options.RemoteRefreshUrl;
});
}
private static void ConfigureUI(WebAssemblyHostBuilder builder)

9
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs

@ -24,8 +24,10 @@ using Volo.Abp.Autofac;
using Volo.Abp.Caching;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Identity;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Security.Claims;
using Volo.Abp.Swashbuckle;
using Volo.Abp.VirtualFileSystem;
@ -105,6 +107,11 @@ public class MyProjectNameHttpApiHostModule : AbpModule
options.RequireHttpsMetadata = configuration.GetValue<bool>("AuthServer:RequireHttpsMetadata");
options.Audience = "MyProjectName";
});
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
});
}
private static void ConfigureSwaggerServices(ServiceConfigurationContext context, IConfiguration configuration)
@ -188,7 +195,7 @@ public class MyProjectNameHttpApiHostModule : AbpModule
{
app.UseMultiTenancy();
}
app.UseDynamicClaims();
app.UseAuthorization();
app.UseSwagger();

7
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs

@ -25,6 +25,7 @@ using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Autofac;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Security.Claims;
using Volo.Abp.Swashbuckle;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.VirtualFileSystem;
@ -74,6 +75,10 @@ public class MyProjectNameHttpApiHostModule : AbpModule
private void ConfigureAuthentication(ServiceConfigurationContext context)
{
context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
});
}
private void ConfigureBundles()
@ -198,7 +203,7 @@ public class MyProjectNameHttpApiHostModule : AbpModule
{
app.UseMultiTenancy();
}
app.UseDynamicClaims();
app.UseUnitOfWork();
app.UseAuthorization();

1
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj

@ -20,6 +20,7 @@
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="8.0.0" />
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="7.0.3" />
</ItemGroup>
<ItemGroup>

10
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs

@ -38,6 +38,7 @@ using Volo.Abp.Identity.Web;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement.Web;
using Volo.Abp.Security.Claims;
using Volo.Abp.SettingManagement.Web;
using Volo.Abp.Swashbuckle;
using Volo.Abp.TenantManagement.Web;
@ -170,7 +171,7 @@ public class MyProjectNameWebModule : AbpModule
* This configuration is used when the AuthServer is running on the internal network such as docker or k8s.
* Configuring the redirecting URLs for internal network and the web
* The login and the logout URLs are configured to redirect to the AuthServer real DNS for browser.
* The token acquired and validated from the the internal network AuthServer URL.
* The token acquired and validated from the the internal network AuthServer URL.
*/
if (configuration.GetValue<bool>("AuthServer:IsContainerized"))
{
@ -209,6 +210,12 @@ public class MyProjectNameWebModule : AbpModule
};
});
}
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
options.RemoteRefreshUrl = configuration["AuthServer:Authority"] + options.RemoteRefreshUrl;
});
}
private void ConfigureAutoMapper()
@ -316,6 +323,7 @@ public class MyProjectNameWebModule : AbpModule
app.UseMultiTenancy();
}
app.UseDynamicClaims();
app.UseAuthorization();
app.UseSwagger();
app.UseAbpSwaggerUI(options =>

6
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs

@ -30,6 +30,7 @@ using Volo.Abp.Identity.Web;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement.Web;
using Volo.Abp.Security.Claims;
using Volo.Abp.SettingManagement.Web;
using Volo.Abp.Swashbuckle;
using Volo.Abp.TenantManagement.Web;
@ -115,6 +116,10 @@ public class MyProjectNameWebModule : AbpModule
private void ConfigureAuthentication(ServiceConfigurationContext context)
{
context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
});
}
private void ConfigureUrls(IConfiguration configuration)
@ -230,6 +235,7 @@ public class MyProjectNameWebModule : AbpModule
app.UseMultiTenancy();
}
app.UseDynamicClaims();
app.UseUnitOfWork();
app.UseAuthorization();
app.UseSwagger();

Loading…
Cancel
Save