Hi,
We are using Blazor WASM with multitenancy and the 'default' security. There is an issue with user roles whereby we cannot add a role to a user. When we try, we get the following error:
System.InvalidOperationException: Sequence contains more than one element.
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9.IsInRoleAsync(TUser user, String normalizedRoleName, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Identity.UserManager`1.AddToRolesAsync(TUser user, IEnumerable`1 roles)
at STracker.Controllers.ApplicationUsersController.Patch(String key, ExpandoObject json) in C:\Dev\stracker\server\Controllers\ApplicationUsersController.cs:line 140
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask).........................
We have three tenants and each tenant has two roles - administrator and user.
I removed all roles except for one tenant which then allowed me to add roles BUT role based page authorization still fails and redirect to unauthorized. Still investigating but I assume it's related.
Hi @korchev , I see that there has been a fix released for Roles cannot be assigned to user in multi-tenant applications. How do I apply that update to my code? I tried running the code in the updated UI but nothing has changed in my code. Is it just a library update?
The fix in Radzen and the generated code when you run the application. Make sure you donβt have files in application ignore list to get the latest code.
I just removed the exclude list and ran in Radzen but don't see any changes showing in git other than my exclude files. Do I need to drop and re-add security in the Radzen UI?
Further to this issue, the Radzen UI did not include 'builder.Services.AddAuthorizationCore();' in Program.cs which seems to be the cause of the page authorization failing.
We can't run the application without your database. Also according to our tests and prior experience AddAuthorizationCore is not required to enable authorization.
I am attaching a test application which works as expected. To run it you need to create the Sample Radzen DB available in the new MSSQL data source screen via the Create Sample Schema button. Go edit the existing data source. It has a page called AdminOnly which is accessible only to users with the role Admin. After creating the database you need to add a tenant (or more) and add the Admin role and then a user with that role. MultiTenantWebAssembly.zip (119.1 KB)
Other than that everything was straight out of the box. The page authorization did not work until I added AddAuthorizationCore and, even stranger, does not work at all after changing the user roles. I've tried different browsers, clearing caches etc but cannot pinpoint the problem.
I'll update after trying your project on my pc.
Update:
Your example app worked OK until I added a second role. Now the user cannot access the AdminOnly page. There appears to be something wrong with multiple roles.
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text.Json;
using System.Threading.Tasks;
namespace MultiTenantWebAssembly.Client
{
public class RolesClaimsPrincipalFactory : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
public RolesClaimsPrincipalFactory(IAccessTokenProviderAccessor accessor) : base(accessor)
{
}
public override async ValueTask<ClaimsPrincipal> CreateUserAsync(RemoteUserAccount account, RemoteAuthenticationUserOptions options)
{
var user = await base.CreateUserAsync(account, options);
if (user.Identity.IsAuthenticated)
{
var identity = (ClaimsIdentity)user.Identity;
var roleClaims = identity.FindAll(identity.RoleClaimType).ToList();
if (roleClaims != null && roleClaims.Any())
{
foreach (var existingClaim in roleClaims)
{
identity.RemoveClaim(existingClaim);
}
var rolesElem = account.AdditionalProperties[identity.RoleClaimType];
if (rolesElem is JsonElement roles)
{
if (roles.ValueKind == JsonValueKind.Array)
{
foreach (var role in roles.EnumerateArray())
{
identity.AddClaim(new Claim(options.RoleClaim, role.GetString()));
}
}
else
{
identity.AddClaim(new Claim(options.RoleClaim, roles.GetString()));
}
}
}
}
return user;
}
}
}
Thanks @simon! We will update our templates with your code and the fix will be part of our next update later this week, until then you can add the file to application ignore list.