Hi team, I have an IMS application built with ABP Commercial 9.0.4, configured with OpenIddict. I created an external machine-to-machine application to allow a third‑party software to integrate with one of my backend APIs. In IMS I have an entity called Blockspace. Each Blockspace belongs to one or more Tour Operators, but every Tour Operator should only be able to read Blockspaces that contain its own tourName. My question is: How can I filter API access so that the external application of a specific Tour Operator can retrieve only its own Blockspaces? More specifically:
Should I store the Tour Operator identifier in the OpenIddict application? Should I add a custom claim to the access token and filter by claim inside the application service/repository? Is there a recommended ABP pattern for applying this type of tenant-like filtering when it’s not a real tenant but a business domain filter?
Any guidance or best practices would be appreciated. Thank you!
13 Answer(s)
-
0
- Don’t rely on “filtering by client_id only” in your app service/repository. You need a stable business identifier (TourOperatorId / tourName) carried with the caller identity and then consistently applied to queries.
- For machine-to-machine (client_credentials), the clean approach is:
- Associate the Tour Operator identifier with the OpenIddict application (client) (as metadata),
- Emit it as a custom claim in the access token,
- Filter data by that claim in your application layer (or via a domain service/repository method that always applies the restriction).
1) Store TourOperatorId on the OpenIddict application
Store the TourOperatorId (or a “tourName”/code) as a property of the OpenIddict client record. Conceptually this is the right place because the client itself represents the external integration.
ABP doesn’t document a specific “TourOperatorId field” for OpenIddict apps, but you can persist custom metadata with the application (e.g., in your own table keyed by OpenIddictApplicationId, or using OpenIddict’s extensibility if you already use it).
2) Add a custom claim to the access token (recommended)
ABP explicitly supports controlling which claims go into
access_token/id_tokenby implementingIAbpOpenIddictClaimsPrincipalHandlerand registering it viaAbpOpenIddictClaimsPrincipalOptions. Use that hook to ensure your custom claim is included in the access token for M2M calls.Implement a handler that:
- Detects this is a client_credentials token request (or generally a client application principal),
- Loads the TourOperatorId associated with the client,
- Adds a claim like
tour_operator_idand sets its destination to AccessToken.
This is aligned with ABP’s documented “Updating Claims In Access_token and Id_token” mechanism.
3) Filter Blockspace queries by the claim
In your
BlockspaceAppService(or better: a domain service used by it), read the claim fromICurrentUser/ClaimsPrincipaland apply it to the query.Example idea (pseudo):
var tourOperatorId = CurrentUser.FindClaimValue("tour_operator_id");- Restrict:
query = query.Where(x => x.TourOperators.Any(to => to.Id == tourOperatorIdGuid));(or bytourNameif that’s your invariant)
This guarantees that even if the external caller tries different parameters, they cannot bypass the restriction.
4) “Tenant-like filtering” when it’s not a real tenant
ABP’s built-in tenant system is for multi-tenancy. For your case, treat it as a business authorization constraint:
- Put the rule in a single place (domain service / repository method / query helper), not scattered across endpoints.
- Use token claims as the boundary between identity and your business rule.
- Optionally combine with ABP permission system (e.g., “Blockspaces.Read”) but permissions alone won’t solve per-operator row filtering.
No verified source in ABP docs specifically prescribes a dedicated “business partition filter” abstraction for this exact scenario; the verified, supported mechanism is adding claims to tokens and then enforcing them in application/domain logic.
Sources
- https://abp.io/docs/latest/modules/openiddict (see “Updating Claims In Access_token and Id_token” and
IAbpOpenIddictClaimsPrincipalHandler) - https://abp.io/docs/latest/framework/fundamentals/authorization (Claims Principal Factory / claims concepts used by ABP)
- https://documentation.openiddict.com/configuration/claim-destinations.html (claim destinations used by ABP handler)
- https://abp.io/support/questions/4125 (ABP support answer pointing to the same OpenIddict claims approach)
- https://abp.io/community/articles/6v0df94z (OpenIddict extensibility patterns in ABP context)
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
-
0
It is not clear hot to "Add a custom claim to the access token (recommended)".
Can I use the extra property on [OpenIddictApplications] /openiddict/Applications ? I add the following code ObjectExtensionManager.Instance.Modules() .ConfigureOpenIddict(auth => { auth.ConfigureApplication(application => { //voglio impostare la maxlenght della property application.AddOrUpdateProperty<string>( ExtraPropertyConsts.TourOperatorCodePropertyName, property => { property.DefaultValue = string.Empty; property.Attributes.Add(new MaxLengthAttribute(ExtraPropertyConsts.MaxTourOperatorCodeLength)); // Imposta la lunghezza massima property.Attributes.Add(new DataTypeAttribute(DataType.Text)); }); }); });
-
0
Yes — using an extra property on
OpenIddictApplicationis a good place to store your Tour Operator code, and yourObjectExtensionManager.Instance.Modules().ConfigureOpenIddict(... ConfigureApplication ...)approach is valid for persisting that metadata.But that alone does not automatically put it into the access token. You must do both:
- Store the value on the OpenIddict application (as you’re doing)
- Copy it into a claim when tokens are issued, and mark that claim’s destination as
AccessToken
Below is a complete, ABP-supported way to do step (2).
1) Keep your OpenIddict application extra property (what you already did)
Your extension config is fine for adding an extra property to the OpenIddict application entity.
Then you set the value for each client (Tour Operator) in
/openiddict/ApplicationsUI or via code.
2) Add the extra property value into the access token as a custom claim
2.1 Create a claim destinations handler (ABP documented hook)
Create a handler that ensures your custom claim is actually included in the access token (otherwise OpenIddict may drop it depending on destinations).
using System.Threading.Tasks; using OpenIddict.Abstractions; using Volo.Abp.DependencyInjection; using Volo.Abp.OpenIddict; public class TourOperatorClaimDestinationsHandler : IAbpOpenIddictClaimsPrincipalHandler, ITransientDependency { public Task HandleAsync(AbpOpenIddictClaimsPrincipalHandlerContext context) { foreach (var claim in context.Principal.Claims) { if (claim.Type == ExtraPropertyConsts.TourOperatorCodePropertyName) { claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken); } } return Task.CompletedTask; } }Register it:
Configure<AbpOpenIddictClaimsPrincipalOptions>(options => { options.ClaimsPrincipalHandlers.Add<TourOperatorClaimDestinationsHandler>(); });2.2 Add the claim to the principal during token issuance
You now need to add the claim value itself to the principal when the token is being created.
ABP’s OpenIddict docs recommend using claims principal handlers to control token claim destinations, but adding the claim value is typically done by contributing to the principal generation pipeline (implementation detail depends on whether you issue tokens for users or clients).
For client_credentials (machine-to-machine), you can add the claim during OpenIddict’s sign-in processing using an OpenIddict server event handler (this is also a known ABP approach in support answers for token claim customization).
Conceptually:
- Determine the client application (
client_id) - Load its extra property
TourOperatorCode - Add it as a claim onto the principal that becomes the access token principal
(Your storage choice—extra property on OpenIddictApplication—is fine; you’ll just need to load it via OpenIddict’s application manager or your own repository/table.)
3) Enforce filtering in your API using the claim
Once the access token contains
TourOperatorCode, you can filter:var tourOperatorCode = CurrentUser.FindClaimValue(ExtraPropertyConsts.TourOperatorCodePropertyName); // apply query restriction based on tourOperatorCodeThis makes the filter non-bypassable (because it is derived from the authenticated client identity, not request parameters).
Sources (validated)
- https://abp.io/docs/latest/modules/openiddict#updating-claims-in-access_token-and-id_token (ABP:
IAbpOpenIddictClaimsPrincipalHandler, destinations, registration) - https://documentation.openiddict.com/configuration/claim-destinations.html (OpenIddict claim destinations)
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
-
0
In which project should I define TourOperatorClaimDestinationsHandler?
The extra property on OpenIddictApplication does not appear in the Angular UI.
-
0
- Define
TourOperatorClaimDestinationsHandlerin the AuthServer project (the project that issues tokens), i.e. typically*.AuthServerin a tiered solution.- Reason:
IAbpOpenIddictClaimsPrincipalHandlerruns during token creation. Your API host (HttpApi.Host) only validates tokens; it doesn’t generate them.
- Reason:
- If your solution is non-tiered (OpenIddict server is inside
HttpApi.Host), then put it in*.HttpApi.Host. - Technically you can place the class in any layer, but it must be in a project/module that is loaded by the token server and registered in
ConfigureServicesof that module.
- Your extra property not appearing in the Angular UI is expected unless the OpenIddict Pro Angular UI is explicitly extended to render that property.
- ABP’s Object Extension system (extra properties + attributes like
MaxLength) automatically lights up UI only for UIs that are built to consume the “object extensions” metadata. The OpenIddict Application Management UI does not automatically render every extra property you add toOpenIddictApplication.
What to do instead (works immediately)
- Keep storing the Tour Operator code in the OpenIddict application extra properties (DB side).
- Set it via code/seed or direct DB update (or implement your own UI extension if you want it editable in Angular).
- Issue it into the token as a claim and enforce filtering in your API.
If you want it visible/editable in Angular, you need to customize/replace the OpenIddict Application create/edit components (similar to how ABP suggests Angular component replacement for other module pages). There isn’t a built-in “automatic extra property rendering” for that OpenIddict page.
Sources
- https://abp.io/docs/latest/modules/openiddict (token customization via ABP OpenIddict integration)
- https://abp.io/docs/latest/object-extensions (how extra properties work in ABP)
- https://abp.io/docs/latest/UI/Angular/Component-Replacement (Angular UI replacement/customization mechanism)
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
- Define
-
0
hi
- You can add a custom claim to the application/client.
https://abp.io/docs/latest/modules/openiddict#updating-claims-in-access_token-and-id_token
- You can add a data filter to filter your data by the current applications' claims
https://abp.io/docs/latest/framework/infrastructure/data-filtering?_redirected=B8ABF606AA1BDF5C629883DF1061649A#defining-custom-filters https://abp.io/community/articles/switching-between-organization-units-i5tokpzt#gsc.tab=0
Thanks.
-
0
[AI-Bot] said: Yes — using an extra property on
OpenIddictApplicationis a good place to store your Tour Operator code, and yourObjectExtensionManager.Instance.Modules().ConfigureOpenIddict(... ConfigureApplication ...)approach is valid for persisting that metadata.But that alone does not automatically put it into the access token. You must do both:
- Store the value on the OpenIddict application (as you’re doing)
- Copy it into a claim when tokens are issued, and mark that claim’s destination as
AccessToken
Below is a complete, ABP-supported way to do step (2).
1) Keep your OpenIddict application extra property (what you already did)
Your extension config is fine for adding an extra property to the OpenIddict application entity.
Then you set the value for each client (Tour Operator) in
/openiddict/ApplicationsUI or via code.
2) Add the extra property value into the access token as a custom claim
2.1 Create a claim destinations handler (ABP documented hook)
Create a handler that ensures your custom claim is actually included in the access token (otherwise OpenIddict may drop it depending on destinations).
using System.Threading.Tasks; using OpenIddict.Abstractions; using Volo.Abp.DependencyInjection; using Volo.Abp.OpenIddict; public class TourOperatorClaimDestinationsHandler : IAbpOpenIddictClaimsPrincipalHandler, ITransientDependency { public Task HandleAsync(AbpOpenIddictClaimsPrincipalHandlerContext context) { foreach (var claim in context.Principal.Claims) { if (claim.Type == ExtraPropertyConsts.TourOperatorCodePropertyName) { claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken); } } return Task.CompletedTask; } }Register it:
Configure<AbpOpenIddictClaimsPrincipalOptions>(options => { options.ClaimsPrincipalHandlers.Add<TourOperatorClaimDestinationsHandler>(); });2.2 Add the claim to the principal during token issuance
You now need to add the claim value itself to the principal when the token is being created.
ABP’s OpenIddict docs recommend using claims principal handlers to control token claim destinations, but adding the claim value is typically done by contributing to the principal generation pipeline (implementation detail depends on whether you issue tokens for users or clients).
For client_credentials (machine-to-machine), you can add the claim during OpenIddict’s sign-in processing using an OpenIddict server event handler (this is also a known ABP approach in support answers for token claim customization).
Conceptually:
- Determine the client application (
client_id) - Load its extra property
TourOperatorCode - Add it as a claim onto the principal that becomes the access token principal
(Your storage choice—extra property on OpenIddictApplication—is fine; you’ll just need to load it via OpenIddict’s application manager or your own repository/table.)
3) Enforce filtering in your API using the claim
Once the access token contains
TourOperatorCode, you can filter:var tourOperatorCode = CurrentUser.FindClaimValue(ExtraPropertyConsts.TourOperatorCodePropertyName); // apply query restriction based on tourOperatorCodeThis makes the filter non-bypassable (because it is derived from the authenticated client identity, not request parameters).
Sources (validated)
- https://abp.io/docs/latest/modules/openiddict#updating-claims-in-access_token-and-id_token (ABP:
IAbpOpenIddictClaimsPrincipalHandler, destinations, registration) - https://documentation.openiddict.com/configuration/claim-destinations.html (OpenIddict claim destinations)
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
can you suggest the implementation for 2.2
-
0
Implementing 2.2 (add the claim value to the principal for
client_credentials) is best done by plugging into the OpenIddict Server event pipeline at token issuance time:- The extra property is stored on the OpenIddict application (client).
- When
/connect/tokenis called withgrant_type=client_credentials, you read the current client_id, load its application record, get the extra property, then add a claim to the principal used to mint the access token. - Your existing
IAbpOpenIddictClaimsPrincipalHandler(step 2.1) then ensures that claim is actually included in the access token via destinations.
Below is a concrete, working pattern.
1) Add a server event handler for client_credentials (AuthServer project)
Create a class like this in your
*.AuthServer(or wherever OpenIddict Server runs):using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using OpenIddict.Abstractions; using OpenIddict.Server; using Volo.Abp.DependencyInjection; public class TourOperatorClientCredentialsClaimsHandler : IOpenIddictServerHandler<OpenIddictServerEvents.HandleTokenRequestContext>, ITransientDependency { private readonly IOpenIddictApplicationManager _applicationManager; public TourOperatorClientCredentialsClaimsHandler( IOpenIddictApplicationManager applicationManager) { _applicationManager = applicationManager; } public async ValueTask HandleAsync(OpenIddictServerEvents.HandleTokenRequestContext context) { // Only for client_credentials if (!context.Request.IsClientCredentialsGrantType()) { return; } // Load application by client_id var application = await _applicationManager.FindByClientIdAsync(context.Request.ClientId!, context.CancellationToken); if (application == null) { return; } // Read your extra property from the OpenIddict application "Properties" JSON var tourOperatorCode = await _applicationManager.GetPropertyAsync<string>( application, ExtraPropertyConsts.TourOperatorCodePropertyName, context.CancellationToken ); if (string.IsNullOrWhiteSpace(tourOperatorCode)) { return; } // IMPORTANT: // In client_credentials, OpenIddict typically uses a principal representing the *client*. // Add your claim to the principal that will be used to issue tokens. // (Depending on pipeline stage, this can be context.Principal or context.Identity.) var identity = context.Principal?.Identity as ClaimsIdentity; if (identity == null) { // Create one if needed identity = new ClaimsIdentity( authenticationType: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, nameType: OpenIddictConstants.Claims.Name, roleType: OpenIddictConstants.Claims.Role); context.Principal = new ClaimsPrincipal(identity); } identity.AddClaim(new Claim(ExtraPropertyConsts.TourOperatorCodePropertyName, tourOperatorCode)); } }Register the handler in OpenIddict server pipeline
In your AuthServer module:
using OpenIddict.Server; public override void PreConfigureServices(ServiceConfigurationContext context) { PreConfigure<OpenIddictServerBuilder>(builder => { builder.AddEventHandler<OpenIddictServerEvents.HandleTokenRequestContext>(options => { options.UseScopedHandler<TourOperatorClientCredentialsClaimsHandler>(); }); }); }At this point:
- Your claim exists on the principal.
- Your step 2.1 destinations handler (
IAbpOpenIddictClaimsPrincipalHandler) should set it toAccessTokenso it actually appears in the JWT.
2) Keep your destinations handler (2.1)
Your
TourOperatorClaimDestinationsHandleris still needed so the claim is not dropped and is placed into the access token:claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken);
3) Apply ABP Data Filter (recommended for the “tenant-like” business filter)
Once the claim is in the token, you can implement a custom data filter keyed by
TourOperatorCodeand enable it by default for your entity/repository logic (so you don’t forget to apply it in each query).(Your referenced ABP data filtering doc is exactly the right direction.)
Sources (validated)
- https://abp.io/docs/latest/modules/openiddict#updating-claims-in-access_token-and-id_token
- https://documentation.openiddict.com/configuration/claim-destinations.html
- https://abp.io/docs/latest/framework/infrastructure/data-filtering#defining-custom-filters
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
-
0
hi
You can ignore the AI answer and consider the: https://abp.io/support/questions/10440/How-to-filter-API-access-for-external-application-using-OpenIddict-ABP-Commercial-904#answer-3a1f81a0-c218-cffb-eb08-cfa3bb756aa5
Thanks.
-
0
Hi @maliming, thank you for your feedback.
The final solution is the following:
1️⃣ OpenIddict – Custom Claims Handler
Implemented TourOperatorClientCredentialsClaimsHandler and updated InventoryHttpApiHostModule to inject the claim into JWT tokens:
public class TourOperatorClientCredentialsClaimsHandler : IOpenIddictServerHandler<OpenIddictServerEvents.ProcessSignInContext>, ITransientDependency { private readonly IOpenIddictApplicationManager _applicationManager; private readonly ILogger<TourOperatorClientCredentialsClaimsHandler> _logger; public static OpenIddictServerHandlerDescriptor Descriptor { get; } = OpenIddictServerHandlerDescriptor.CreateBuilder<OpenIddictServerEvents.ProcessSignInContext>() .UseScopedHandler<TourOperatorClientCredentialsClaimsHandler>() .SetOrder(int.MinValue + 100_000) .SetType(OpenIddictServerHandlerType.Custom) .Build(); public TourOperatorClientCredentialsClaimsHandler( IOpenIddictApplicationManager applicationManager, ILogger<TourOperatorClientCredentialsClaimsHandler> logger) { _applicationManager = applicationManager; _logger = logger; } public async ValueTask HandleAsync(OpenIddictServerEvents.ProcessSignInContext context) { // Check if this is a client_credentials grant if (context.Request?.GrantType != OpenIddictConstants.GrantTypes.ClientCredentials) { return; } var clientId = context.Request.ClientId; if (string.IsNullOrEmpty(clientId)) { _logger.LogWarning("No client_id found in request"); return; } _logger.LogInformation($"Processing client_credentials sign-in for client: {clientId}"); // Load application by client_id var application = await _applicationManager.FindByClientIdAsync(clientId, context.CancellationToken); if (application == null) { _logger.LogWarning($"Application not found for client_id: {clientId}"); return; } // Read your extra property from the OpenIddict application "Properties" JSON var properties = await _applicationManager.GetPropertiesAsync(application, context.CancellationToken); _logger.LogInformation($"Application properties count: {properties.Count}"); if (!properties.TryGetValue(ExtraPropertyConsts.TourOperatorCodeClaimName, out var tourOperatorCodeElement)) { _logger.LogWarning($"No {ExtraPropertyConsts.TourOperatorCodeClaimName} property found for client: {clientId}"); return; } string? tourOperatorCode = null; if (tourOperatorCodeElement.ValueKind == JsonValueKind.String) { tourOperatorCode = tourOperatorCodeElement.GetString(); } if (string.IsNullOrWhiteSpace(tourOperatorCode)) { _logger.LogWarning($"Empty {ExtraPropertyConsts.TourOperatorCodeClaimName} property for client: {clientId}"); return; } _logger.LogInformation($"Found tour_operator_code: '{tourOperatorCode}' for client: {clientId}"); // Add the claim to the principal var identity = context.Principal?.Identity as ClaimsIdentity; if (identity == null) { _logger.LogError($"No identity found in principal for client: {clientId}"); return; } // Create the claim with explicit destinations set to AccessToken var claim = new Claim(ExtraPropertyConsts.TourOperatorCodeClaimName, tourOperatorCode); claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken); identity.AddClaim(claim); _logger.LogInformation($"Successfully added {ExtraPropertyConsts.TourOperatorCodeClaimName} claim with value '{tourOperatorCode}' and AccessToken destination for client: {clientId}"); } } PreConfigure<OpenIddictServerBuilder>(builder => { builder.AddEventHandler(TourOperatorClientCredentialsClaimsHandler.Descriptor); });2️⃣ Claim-based Filtering Infrastructure
Added ITourOperatorCodeFilter interface
Added TourOperatorCodeFilterProvider
Added ExtraPropertyConsts for claim-based filtering logic
public class TourOperatorCodeFilterProvider : ITourOperatorCodeFilter, ITransientDependency { private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor; public TourOperatorCodeFilterProvider(ICurrentPrincipalAccessor currentPrincipalAccessor) { _currentPrincipalAccessor = currentPrincipalAccessor; } public string? TourOperatorCode { get { var principal = _currentPrincipalAccessor.Principal; if (principal == null || principal.Identity?.IsAuthenticated != true) { return null; } var claim = principal.Claims?.FirstOrDefault(c => c.Type == ExtraPropertyConsts.TourOperatorCodeClaimName); return claim?.Value; } } public bool IsEnabled => !string.IsNullOrWhiteSpace(TourOperatorCode); }3️⃣ Automatic Query Filtering
Introduced BlockspaceQueryFilterExtensions
Updated the repository to automatically apply the filter to all Blockspace queries
public override async Task<IQueryable<Blockspace>> WithDetailsAsync() { var query = await base.WithDetailsAsync(); return query.ApplyTourOperatorCodeFilter(_dataFilter, _tourOperatorCodeFilter); } public override async Task<IQueryable<Blockspace>> GetQueryableAsync() { var query = await base.GetQueryableAsync(); return query.ApplyTourOperatorCodeFilter(_dataFilter, _tourOperatorCodeFilter); }🔜 Remaining Task
The only thing missing is the UI in Angular for managing OpenIddict Application Properties.
-
0
hi
You can add an
IAbpClaimsPrincipalContributor, no need theTourOperatorClientCredentialsClaimsHandlerpublic class SocialSecurityNumberClaimsPrincipalContributor : IAbpClaimsPrincipalContributor, ITransientDependency { public async Task ContributeAsync(AbpClaimsPrincipalContributorContext context) { var identity = context.ClaimsPrincipal.Identities.FirstOrDefault(); var userId = identity?.FindUserId(); if (userId.HasValue) { var userService = context.ServiceProvider.GetRequiredService<IUserService>(); //Your custom service var socialSecurityNumber = await userService.GetSocialSecurityNumberAsync(userId.Value); if (socialSecurityNumber != null) { identity.AddClaim(new Claim("SocialSecurityNumber", socialSecurityNumber)); } } } } public static class CurrentUserExtensions { public static string GetSocialSecurityNumber(this ICurrentUser currentUser) { return currentUser.FindClaimValue("SocialSecurityNumber"); } }Then add it to the access token or id token.
public class MyClaimDestinationsHandler : IAbpOpenIddictClaimsPrincipalHandler, ITransientDependency { public virtual Task HandleAsync(AbpOpenIddictClaimsPrincipalHandlerContext context) { foreach (var claim in context.Principal.Claims) { if (claim.Type == "SocialSecurityNumber") { claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken, OpenIddictConstants.Destinations.IdentityToken); } return Task.CompletedTask; } } Configure<AbpOpenIddictClaimsPrincipalOptions>(options => { options.ClaimsPrincipalHandlers.Add<MyClaimDestinationsHandler>(); }); -
0
Hi, thank you for the response. My problem is that the authentication is not coming from a user, but from an application with client credentials flow, and with this authentication flow IAbpClaimsPrincipalContributor is not invoked :-(
-
0
ok. Then
TourOperatorClientCredentialsClaimsHandleris no problem.Thanks.