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!


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

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.2.0-preview. Updated on February 17, 2026, 09:10
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.