Reputation: 31
I'm using Identity in Blazor app. I sed table AspNetRoles with my 3 roles: "user" "administrator" "moderator". And on succesfully registration there is creating relation in AspNetUserRoles between role and user. All works to this moment, but when Im trying to check role with
@attribute [Authorize(Roles = "user")]
or
<AuthorizeView Roles="user">...
It doesn't see roles and I always get NotAuthorized view. Should I add this roles in any way in Startup.cs or sth? Here is my startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddSingleton<TableManager>();
/* services.AddSingleton<ScoreManager>();*/
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddAuthorization(options =>
{
options.AddPolicy("user", policy => policy.RequireRole("user"));
});
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddRazorPages();
services.AddTransient<RolesSeeder>();
}
Policy doesnt work too, when I use @attribute[Authorize(Policy="user")]
or same wwith I'm getting error "An unhandled error has occurred. Reload".
Edit 1:
Roles seeder:
public class RolesSeeder
{
private ApplicationDbContext dbContext;
public RolesSeeder(ApplicationDbContext dbContext)
{
this.dbContext = dbContext;
}
public async void SeedRoles()
{
var roleStore = new RoleStore<IdentityRole>(dbContext);
if(!(dbContext.Roles.Any(r => r.Name == "administrator")))
{
await roleStore.CreateAsync(new IdentityRole { Name = "administrator", NormalizedName = "administrator" });
}
if (!(dbContext.Roles.Any(r => r.Name == "user")))
{
await roleStore.CreateAsync(new IdentityRole { Name = "user", NormalizedName = "user" });
}
if (!(dbContext.Roles.Any(r => r.Name == "moderator")))
{
await roleStore.CreateAsync(new IdentityRole { Name = "moderator", NormalizedName = "moderator" });
}
await dbContext.SaveChangesAsync();
}
}
Added in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<RolesSeeder>();
Register.cshtml added:
if (user.UserName.Contains("admin"))
{
await _userManager.AddToRoleAsync(user, "administrator");
}
if (user.UserName.Contains("moderator"))
{
await _userManager.AddToRoleAsync(user, "moderator");
}
else
{
await _userManager.AddToRoleAsync(user, "user");
}
Upvotes: 0
Views: 513
Reputation: 14623
In the client add:
builder.Services.AddApiAuthorization().AddAccountClaimsPrincipalFactory<CustomUserFactory>();
public class CustomUserFactory : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
public CustomUserFactory(IAccessTokenProviderAccessor accessor)
: base(accessor)
{
}
public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
RemoteUserAccount account,
RemoteAuthenticationUserOptions options)
{
var user = await base.CreateUserAsync(account, options);
ClaimsIdentity claimsIdentity = (ClaimsIdentity)user.Identity;
if (account is not null) {
MapArrayClaimsToMultipleSeparateClaims(account, claimsIdentity);
}
return user;
}
private void MapArrayClaimsToMultipleSeparateClaims(RemoteUserAccount account, ClaimsIdentity claimsIdentity)
{
foreach (var keyValuePair in account.AdditionalProperties) {
var key = keyValuePair.Key;
var value = keyValuePair.Value;
if (value is not null &&
value is JsonElement element && element.ValueKind == JsonValueKind.Array) {
claimsIdentity.RemoveClaim(claimsIdentity.FindFirst(keyValuePair.Key));
var claims = element.EnumerateArray()
.Select(x => new Claim(keyValuePair.Key, x.ToString()));
claimsIdentity.AddClaims(claims);
}
}
}
}
If your seeding roles after login. The relevant user needs to logout then in again to have the claims.
Upvotes: 1