How to actually implement Roles in Blazor WebApp and Identity?

397 Views Asked by At

So, here's the thing: If you create a Blazor WebApp (.Net8) with individual accounts enabled, it will give you a working solution. For development, I'm using Sqlite. Here's what that looks like (in an otherwise completely functional app):

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite("Data Source=Application.db"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddSignInManager()
    .AddDefaultTokenProviders();

Please take note of the Core in AddIdentityCore. Now, the helptext for this method states that you simply should use AddRole<IdentityRole> if you want roles. An inspection of the database also yields that there are automatically created tables for roles (and claims).

However, when you add the AddRoles<IdentityRole> it then bombs out because the RoleManager service is missing. Easy fix: Just do AddRoleManager<RoleManager<IdentityRole>>

Which in turn bombs out because the RoleStore service is missing. Okay. So I do AddRoleStore<IdentityRole> and everything is peachy, right?

Nope: Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContext' while attempting to activate 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.RoleStore'

Which is weird because every other service finds the DbContext just fine. So, where do I go from here?

Oh, and using other things like AddIdentity lets the app bomb out at other locations. I'm a bit irritated because all the documentation available makes no mention of AddIdentityCore and also makes no mention of the necessity to add the manager and the store, instead an AddRole suffices in those examples.

3

There are 3 best solutions below

0
Rhywden On BEST ANSWER

By inspecting the definition of Microsoft.AspNetCore.Identity.EntityFrameworkCore.RoleStore I found that it has several overloads, one of which takes a DbContext.

For some reason the default project insists (for me) on adding everything (i.e. Roles, RoleManager and RoleStore) and throws an exception if I leave one of the three out. So the whole definition has to look like this for me:

builder.Services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddSignInManager()
    .AddRoles<IdentityRole>()
    .AddRoleManager<RoleManager<IdentityRole>>()
    .AddRoleStore<RoleStore<IdentityRole, ApplicationDbContext>>()
    .AddDefaultTokenProviders();
1
Qiang Fu On

This works in my test for AddIdentityCore

builder.Services.AddControllers();
...
builder.Services.AddIdentityCore<ApplicationUser>()
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddSignInManager()
    .AddRoleManager<RoleManager<IdentityRole>>()
    .AddDefaultTokenProviders();
...
app.MapControllers();

Controller

    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly RoleManager<IdentityRole> _roleManager;

        public ValuesController(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
        {
            this._userManager = userManager;
            this._roleManager = roleManager;
        }
        [HttpGet]
        [Route("test")]
        public async Task<IActionResult> RegisterAdmin()
        {
            ApplicationUser user = new()
            {
                Email = "[email protected]",
                UserName = "aa"
            };
            var result = await _userManager.CreateAsync(user, "Q!q11111");
            await _roleManager.CreateAsync(new IdentityRole("Admin"));        
            await _userManager.AddToRoleAsync(user, "Admin");
           
            return Ok(new { Status = "Success", Message = "User created successfully!" });
        }
    }

Test result
enter image description here

0
Gerardo.NYC On

In AddIdentityCore() you must call

.AddRoles<IdentityRole>()

before

.AddEntityFrameworkStores<ApplicationDbContext>()

or it won't work.