Open Closed

How to filter API access for external application using OpenIddict (ABP Commercial 9.0.4) #10440


User avatar
0
s4lv0 created

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)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • 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:
      1. Associate the Tour Operator identifier with the OpenIddict application (client) (as metadata),
      2. Emit it as a custom claim in the access token,
      3. 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_token by implementing IAbpOpenIddictClaimsPrincipalHandler and registering it via AbpOpenIddictClaimsPrincipalOptions. 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_id and 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 from ICurrentUser / ClaimsPrincipal and 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 by tourName if 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.

  • User Avatar
    0
    s4lv0 created

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

  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent

    Yes — using an extra property on OpenIddictApplication is a good place to store your Tour Operator code, and your ObjectExtensionManager.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:

    1. Store the value on the OpenIddict application (as you’re doing)
    2. 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/Applications UI 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 tourOperatorCode
    

    This 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.

  • User Avatar
    0
    s4lv0 created

    In which project should I define TourOperatorClaimDestinationsHandler?

    The extra property on OpenIddictApplication does not appear in the Angular UI.

  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • Define TourOperatorClaimDestinationsHandler in the AuthServer project (the project that issues tokens), i.e. typically *.AuthServer in a tiered solution.
      • Reason: IAbpOpenIddictClaimsPrincipalHandler runs during token creation. Your API host (HttpApi.Host) only validates tokens; it doesn’t generate them.
    • 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 ConfigureServices of 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 to OpenIddictApplication.

    What to do instead (works immediately)

    1. Keep storing the Tour Operator code in the OpenIddict application extra properties (DB side).
    2. Set it via code/seed or direct DB update (or implement your own UI extension if you want it editable in Angular).
    3. 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.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    1. 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

    1. 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.

  • User Avatar
    0
    s4lv0 created

    [AI-Bot] said: Yes — using an extra property on OpenIddictApplication is a good place to store your Tour Operator code, and your ObjectExtensionManager.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:

    1. Store the value on the OpenIddict application (as you’re doing)
    2. 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/Applications UI 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 tourOperatorCode 
    

    This 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

  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent

    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/token is called with grant_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 to AccessToken so it actually appears in the JWT.

    2) Keep your destinations handler (2.1)

    Your TourOperatorClaimDestinationsHandler is 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 TourOperatorCode and 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.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    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.

  • User Avatar
    0
    s4lv0 created

    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.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You can add an IAbpClaimsPrincipalContributor, no need the TourOperatorClientCredentialsClaimsHandler

    public 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>();
    });
    
    
  • User Avatar
    0
    s4lv0 created

    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 :-(

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    ok. Then TourOperatorClientCredentialsClaimsHandler is no problem.

    Thanks.

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.3.0-preview. Updated on March 09, 2026, 08:51
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.