-
ABP Framework version: v7.3.3
-
UI Type: Blazor Server
-
Database System: EF Core SQL Server
-
Tiered (for MVC) or Auth Server Separated (for Angular): Tiered
-
Exception message and full stack trace:
API HOST side
Blazor Side
-
Steps to reproduce the issue:
We created a custom PermissionValueProvider for our specific requirements, and even though the CheckAsync functions appear to return correct result values, it still fails with Forbidden error.
The curious thing is that permissions are required for the user for the menu items, so if I add scopes (our permission system) to the user, the ScopedRolesPermissionValueProvider properly
add those permissions to the user and the user is now able to see the menu items.
But if we run api calls that use the Authorize
attribute, it fails with the Forbidden error.
code for custom PermissionValueProvider:
public class ScopedRolesPermissionValueProvider : IPermissionValueProvider, ITransientDependency
{
public string Name => "ScopedRolePermission";
private const string ADMIN_CLIENT_ID = "Fixhub_Web";
private readonly IServiceProvider _serviceProvider;
public ScopedRolesPermissionValueProvider(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task CheckAsync(PermissionValueCheckContext context)
{
var clientId = context.Principal?.FindFirst(AbpClaimTypes.ClientId)?.Value;
if (clientId is null || clientId == ADMIN_CLIENT_ID)
{
return PermissionGrantResult.Undefined; // We should let other PermissionValueProvider decide...
}
var userId = context.Principal?.FindFirst(AbpClaimTypes.UserId)?.Value;
if (userId == null)
{
return PermissionGrantResult.Undefined;
}
IPermissionContextManager permissionContextManager =
_serviceProvider.GetRequiredService();
PermissionGrantResult permissionGrantResult =
await permissionContextManager.IsScopeGrantedAsync(userId, context.Permission?.Name);
return permissionGrantResult;
}
public async Task CheckAsync(
PermissionValuesCheckContext context
)
{
var permissionNames = context.Permissions.Select(x => x.Name).Distinct().ToArray();
Check.NotNullOrEmpty(permissionNames, nameof(permissionNames));
var userId = context.Principal?.FindFirst(AbpClaimTypes.UserId)?.Value;
if (userId == null)
{
return new MultiplePermissionGrantResult(permissionNames);
} // Returns all permissions as Undefined.
IPermissionContextManager permissionContextManager =
_serviceProvider.GetRequiredService(); // as PermissionContextManager;
return await permissionContextManager.IsScopeGrantedAsync(userId, permissionNames);
}
}
extension method for IsScopeGrantedAsync:
public static async Task IsScopeGrantedAsync(
this IPermissionContextManager manager,
string userId,
string[] permissionNames
)
{
var scopedRolesAllowedPermissions = await manager.GetUserCurrentScopedPermissions(userId);
var result = new MultiplePermissionGrantResult();
foreach (var item in permissionNames)
{
result.Result.Add(
item,
item != null && scopedRolesAllowedPermissions.Contains(item)
? PermissionGrantResult.Granted
: PermissionGrantResult.Undefined
);
}
return result;
}
Results returned seem good:
How we add the dependency in our ApplicationModule.cs file
Basically browsing any page that either has the [Authorize]
attribute or that calls an api endpoint that has the [Authorize]
attribute fails with forbidden even if the user seems to have all the correct permissions assigned by our custom permission value provider.
This only happens through the blazor UI. Going directly through the API is perfectly fine. We even have a completely separate ReactJS app that connects to those endpoints and it works fine.
3 Answer(s)
-
0
Hi, I will check and let you know asap.
Regards.
-
0
I created a new Tiered Blazor project and added a custom permission value provider that simply returns all permissions as granted and it worked fine.
But in our current solution, even the "grant all" approach has the same error.
-
0
It seems that the actual error came from the singular permission grant check and from returning undefined when for the blazor app.
Removing this code seems to have fixed the issue (this is code that used to be useful, but not anymore, in our codebase).
Closing for now, as long as the issue has been resolved.