The PermissionService in Angular calls the Application Configuration Endpoint to obtain user permissions via: "auth": {"grantedPolicies":{...}} The PermissionChecker is used to obtain the permissions. Unfortunately, the logic between the PermissionChecker,IsGrantedAsync overloads are not consistent. The overload for checking a single permission behaves according to the documentation (as described here https://abp.io/docs/latest/framework/fundamentals/authorization?_redirected=B8ABF606AA1BDF5C629883DF1061649A#advanced-topics) and allows all value providers to participate, with any Prohibited result overriding a Granted. However, the overload for checking a list of permissions always returns the first result that is not PermissionGrantResult.Undefined. The means the order in which the value providers are registered matters and the first Granted or Prohibited wins and no further value providers are allowed to provide a result.
Steps to reproduce the issue:
- Define a permission (PermissionA)
- Create a custom PermissionValueProvider that returns PermissionGrantResult.Prohibited; for that permission (PermissionA)
- Register your custom PermissionValueProvider so it is added after the standard ones: options.ValueProviders.Add();
- Create a user and assign them that permission (PermissionA) in the UI
- On the Angular side, add code to check the permission of the user after they have signed in: const check = this.permissionService.getGrantedPolicy('PermissionA');
- On the API side, add code to a check the permission of the user: await this.authorizationService.CheckAsync("PermissionA");
Notice that the permission is granted on the Angular side but not on the API
1 Answer(s)
-
0
- Solution
Root cause
- Angular’s PermissionService reads grantedPolicies from the Application Configuration endpoint (/api/abp/application-configuration). On the backend, this endpoint builds grantedPolicies using IPermissionChecker.IsGrantedAsync(string[] names) to evaluate multiple permissions at once for efficiency.
- In current ABP, the single-permission check and multi-permission check use different resolution flows: for a single permission, all registered PermissionValueProviders participate and any Prohibited overrides Granted (as documented). For multiple permissions, the evaluation short-circuits by returning the first non-Undefined result from the providers, so provider registration order affects the outcome. If your custom PermissionValueProvider that returns Prohibited is registered after a provider that returns Granted, the multi-permission path may never “see” your Prohibited result. This leads to grantedPolicies including PermissionA even though your backend AuthorizationService.CheckAsync("PermissionA") fails (because Check/IsGranted for a single permission considers all providers and respects Prohibited-overrides-Granted).
How to get consistent results (server and Angular)
- Ensure your custom provider runs before the built-ins when building grantedPolicies
- Register your provider at the beginning, so its Prohibited can take precedence in the multi-permission path used by application configuration.
- Example:
- Ensure your custom provider runs before the built-ins when building grantedPolicies
Configure<PermissionOptions>(options => { // Insert at the beginning to ensure it’s checked first options.ValueProviders.Insert(0, typeof(MyCustomPermissionValueProvider)); // If you previously used Add(), it appends to the end — change it to Insert(0, ...) });- Prefer single-permission checks when building grantedPolicies
- If you have overridden the AbpApplicationConfigurationAppService or produce your own grantedPolicies map, ensure you use IPermissionChecker.IsGrantedAsync(string name) per permission instead of the overload that accepts a list. That guarantees the “all providers participate, Prohibited overrides Granted” behavior for each permission:
// Pseudocode inside your configuration builder foreach (var permission in permissionDefinitionManager.GetPermissions()) { var granted = await permissionChecker.IsGrantedAsync(permission.Name); if (granted) { dto.Auth.GrantedPolicies[permission.Name] = true; } }- If you haven’t overridden anything, fixing provider order (step 1) is typically sufficient.- If you must use the multi-permission overload for performance, enforce provider precedence
- Keep using IsGrantedAsync(string[] names) but make sure your custom provider is ordered first (Insert(0, ...)). This ensures the first non-Undefined is your Prohibited result for any permissions it handles.
- Double-check your provider logic
- Return PermissionGrantResult.Prohibited only for the permissions you truly want to block.
- Return PermissionGrantResult.Undefined for everything else so other providers can participate (role, user, client, etc.).
- Verification checklist
- Register your custom provider via Insert(0, typeof(MyCustomPermissionValueProvider)).
- Confirm /api/abp/application-configuration returns auth.grantedPolicies without the prohibited permission.
- Confirm Angular’s this.permissionService.getGrantedPolicy('PermissionA') returns false.
- Confirm backend AuthorizationService.CheckAsync("PermissionA") throws as expected.
- Sources
- https://abp.io/docs/latest/framework/fundamentals/authorization#advanced-topics
- https://github.com/abpframework/abp
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.