Follow-up: channel identification must not be client-supplied
Thank you for the confirmation on the value provider approach — that resolves our main question.
We want to flag one point in the answer that we believe is unsafe for our use case, and ask for guidance on the alternative.
The concern
The response suggests identifying the channel via a request header such as X-Channel: Mobile, transformed into a claim by middleware before permission evaluation. In our scenario this does not hold, because the attacker is the authenticated user themselves.
The user holds a valid token and legitimately holds the permission. The only thing being enforced is which channel they may exercise it from. A request header is fully controlled by the sender, so the restriction can be removed by editing one string:
# blocked
curl -H "Authorization: Bearer <valid-token>" -H "X-Channel: Api" \
-d '{"amount":50000}' https://api.example.com/api/wallet/transfer
# same token, header changed — allowed
curl -H "Authorization: Bearer <valid-token>" -H "X-Channel: Mobile" \
-d '{"amount":50000}' https://api.example.com/api/wallet/transfer
Authentication succeeds, the permission check passes, and the control is bypassed entirely. For a money transfer operation this is not an acceptable enforcement boundary — a security decision cannot rest on an unsigned value asserted by the caller.
Our intended approach
Derive the channel from the token itself rather than from the request. We plan to register a separate OpenIddict client per channel and resolve the channel from the client_id claim on the validated principal:
| Client | Channel | Type |
|---|---|---|
| Wallet_Mobile | Mobile | public |
| Wallet_Web | Web | public |
| Partner_Api | Api | confidential (client secret) |
with an unrecognised or absent client_id defaulting to the least-trusted channel rather than the most permissive one.
We are aware this still has a residual weakness: Wallet_Mobile is a public client, so its client_id can be extracted from the app package and reused from any HTTP client to obtain a token that claims the Mobile channel. We consider this acceptable for now and plan to address it separately through device binding and platform attestation. We raise it only so the recommendation is not read as a hard security boundary by others with the same requirement.
Questions
Is reading AbpClaimTypes.ClientId from ICurrentPrincipalAccessor inside a permission value provider reliable across all execution paths — HTTP requests, background jobs, and distributed event handlers? We want to be sure the principal is populated consistently, since a null principal must not silently fall through to a permissive result.
Is there a recommended ABP pattern for making a claim available to the permission pipeline that we should use instead of, or alongside, IAbpClaimsPrincipalContributor?
Would you consider revising the header-based suggestion in the original answer? Anyone implementing it as written for a permission restriction would end up with a control that is trivially bypassable by the very user it is meant to constrain.
Questions on channel modelling, binding, and administration
Beyond the enforcement mechanism itself, we would like guidance on how the channel concept should be modelled and administered in an ABP-based system. We could not find an existing abstraction for this, and would prefer to align with the framework's intent rather than invent one.
Defining channels
Is there any existing notion in ABP of an "access channel" or request origin that we should build on, or is the concept expected to be introduced entirely by the application?
Would you recommend channels be a static enum defined in code, or a configurable entity stored in the database so administrators can add a channel (for example a new partner integration or a USSD gateway) without a deployment? A static definition is simpler and safer, but it means permission definitions and channel definitions live in different places and drift apart over time.
Should the channel be treated as a first-class concept alongside tenants and users, or as an implementation detail confined to the authorization layer? This affects whether channel identity becomes available for auditing, rate limiting, and transaction limits — which we expect to need — or stays isolated to permission checks.
Binding channels to clients
We plan a one-to-one mapping between an OpenIddict client and a channel. Is there a supported place to store this mapping — client properties, an OpenIddict client extension, application settings, or a dedicated table? We would rather not hardcode a switch on client_id.
How should multiple clients on the same channel be handled? We expect several distinct web applications that should all resolve to the Web channel, and multiple partner integrations each with its own client but all belonging to the Api channel. Is a many-to-one client-to-channel relationship the recommended shape?
Should the channel be exposed as a scope rather than derived from client_id? A scope is requested per token and could give finer control, but it is also negotiable by the client at token request time, which appears weaker for our purpose. We would appreciate your view on the trade-off.
How should tokens issued through flows without a meaningful client be treated — client credentials for internal service-to-service calls, background jobs, and distributed event handlers? Our current intent is to resolve these to a dedicated internal channel that is excluded from channel restrictions entirely, but we would like to know whether that matches the framework's expectations.
Administration
The built-in permission management modal is built around a two-dimensional model (subject × permission). Is there a supported way to extend it with a third dimension, or is a separate management screen the expected path? If a separate screen is expected, is there a recommended way to reuse IPermissionAppService so administrators do not see two disconnected permission UIs?
Should channel restrictions be manageable at the role level as well as the user level? Per-user rules alone will not scale for us — a tenant with thousands of users would need a rule row per user. If role-level restrictions are advisable, what precedence would you recommend between a role-level and a user-level rule?
Are channel restrictions expected to be tenant-scoped? In a multi-tenant deployment, should a host administrator be able to define channel restrictions that a tenant administrator cannot override, and is there an existing ABP pattern for that kind of layered policy?
Is there guidance on auditing changes to these rules? Given that they govern financial operations, we need a durable record of who changed a restriction and when. We assume ABP's entity change auditing covers this, but would like confirmation that it applies cleanly to a custom entity of this kind.
What we are trying to achieve
Our platform is consumed through several channels simultaneously — multiple web applications, mobile applications, and direct API integrations used by partner systems. All of them authenticate against the same auth server and share the same users, roles, and permission definitions.
We need permission granting to keep working exactly as it does today through user and role providers. On top of that, we need a purely subtractive rule: for a user who already holds a permission, that permission may be blocked on certain channels while remaining available on others. A user who does not hold the permission stays blocked everywhere — the channel dimension should never grant anything by itself.
The mapping differs per user. It is not a property of the permission, nor of the channel, but of the combination of user, permission, and channel.
Example
With two channels (Mobile, Api), two users (u1, u2), and two operations (op1, op2), all four permissions granted through roles:
| User | Operation | Mobile | Api | |---|---|---|---| | u1 | op1 | allowed | blocked | | u1 | op2 | blocked | allowed | | u2 | op1 | blocked | allowed | | u2 | op2 | allowed | allowed |
Our questions
Does ABP already provide a built-in way to express this that we may have missed? We looked at ClientPermissionValueProvider, but it appears to grant permissions to a client rather than constrain permissions a user already holds, and it has no per-user dimension.
If there is no built-in support, what is the recommended extension point for this? We would like to follow the approach the ABP team considers correct rather than pick one and discover later that it breaks on upgrade or bypasses part of the pipeline.
Is there a supported way to represent an explicit denial in permission storage? PermissionGrant is keyed on provider name, provider key, and permission name, and we could not find a representation for "granted but blocked in this context."
How should the client side be handled? AbpApplicationConfigurationAppService returns a single permission set per user, so a mobile app and an API caller receive an identical set. Is there a recommended way to make this endpoint channel-aware so the UI does not render actions that will fail at invocation time?
Is this scenario considered in scope for the framework, or is it expected to live entirely in application code? A clear answer here is enough for us to commit to a direction.
Environment
8.3Angular + Mvc + Flutter for mobileYesThis issue is not related to Login info. the problem is module name. ABP Suite (v9.1.0) request old name Volo.Abp.LeptonXTheme.Pro
below commands works normally:
abp get-source Volo.Abp.LeptonXTheme
abp get-source Volo.Abp.LeptonXLiteTheme
Can you try to use abp get-source Volo.Abp.LeptonXTheme?
same error. It's downloaded any other versions, tried 4.3.0-rc.1 and 4.0.0-4.2.0 Any other modules downloaded normally. But LeptonXTheme pro and lite v4.2.2 return this error
OK, thanks for your help.
Yes, and it works fine. But I need it encrypted
Angular app first time login is work fine, but when refresh the page there is an error occured due to reading AccessToken which is encrypted in line remember-me.service.ts
ERROR SyntaxError: Unexpected token 'õ', "õ¿Ø]&¶`"... is not valid JSON
at JSON.parse (<anonymous>)
Steps to reproduce
Suggestion to Solve I can see the id_token is available to read same as access_token when encryption enabled. Try read access_token when error occured read id_token if exists.
Yes, I did that too, but the problem still persists. Were you able to reproduce this issue?
hi, This happens even in MVC. To reproduce as I noticed: you wait (without any actions somtimes) till Token expired but not the cookie, when the Web call HostApi which not accept Web Token. this issue happend.
I tried to change cookie and Token configs but still face it. Most of times Logging out not solve it. Nor clean browser cache. This happens in development and prodctuion environments
Currently The ONLY WORKAROUND FOR THIS : Clean REDIS cache
I think this related to some enryption keys for Token stored in REDIS cache, when clear the cache the system re-generate some keys.
How can implement phantom token with OpenIddect? Please look below url https://curity.io/resources/learn/phantom-token-pattern/