ASP.NET Core Identity 2: User.IsInRole always returns false

So, to recap, the question asks why the code provided by the ASP.NET Core Web Application template doesn't load roles or role claims into the cookie when a user logs in.

After much Googling and experimenting, there appear to be two modifications that must be made to the templated code in order to get Roles and Role Claims to work:

First, you must add the following line of code in Startup.cs to enable RoleManager. (This bit of magic was mentioned in the OP.)

services.AddDefaultIdentity<ApplicationUser>()
   .AddRoles<IdentityRole>() // <-- Add this line
    .AddEntityFrameworkStores<ApplicationDbContext>();

But wait, there's more! According to this discussion on GitHub, getting the roles and claims to show up in the cookie involves either reverting to the service.AddIdentity initialization code, or sticking with service.AddDefaultIdentity and adding this line of code to ConfigureServices:

// Add Role claims to the User object
// See: https://github.com/aspnet/Identity/issues/1813#issuecomment-420066501
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>>();

If you read the discussion referenced above, you'll see that Roles and Role Claims are apparently kind-of-deprecated, or at least not eagerly supported. Personally, I find it really useful to assign claims to roles, assign roles to users, and then make authorization decisions based on the claims (which are granted to the users based on their roles). This gives me an easy, declarative way to allow, for example, one function to be accessed by multiple roles (i.e. all of the roles that contain the claim used to enable that function).

But you DO want to pay attention to the amount of role and claim data being carried in the auth cookie. More data means more bytes sent to the server with each request, and I have no clue what happens when you bump up against some sort of limit to the cookie size.


Ahh, there are some changes from ASP.NET Core version 2.0 to 2.1. AddDefaultIdentity is the one.

I don't know where to start from your code, so, I will provide an example to create and get user role(s).

Let's create UserRoles first:

public enum UserRoles
{
    [Display(Name = "Quản trị viên")]
    Administrator = 0,

    [Display(Name = "Kiểm soát viên")]
    Moderator = 1,

    [Display(Name = "Thành viên")]
    Member = 2
}

Note: You can remove the attribute Display.

Then, we create RolesExtensions class:

public static class RolesExtensions
{
    public static async Task InitializeAsync(RoleManager<IdentityRole> roleManager)
    {
        foreach (string roleName in Enum.GetNames(typeof(UserRoles)))
        {
            if (!await roleManager.RoleExistsAsync(roleName))
            {
                await roleManager.CreateAsync(new IdentityRole(roleName));
            }
        }
    }
}

Next, in the Startup.cs class, we run it:

    public void Configure(
        IApplicationBuilder app, 
        IHostingEnvironment env, 
        RoleManager<IdentityRole> roleManager)
    {
        // other settings...

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });

        var task = RolesExtensions.InitializeAsync(roleManager);
        task.Wait();
    }

Note: Configure requires a returned type void, so we need to create a task to initialize the user roles and we call Wait method.

Do not change the returned type like this:

public async void Configure(...)
{
    await RolesExtensions.InitializeAsync(roleManager);
}

Source: Async/Await - Best Practices in Asynchronous Programming

In the ConfigureServices method, these configurations would NOT work (we cannot use User.IsInRole correctly):

services.AddDefaultIdentity<ApplicationUser>()
    //.AddRoles<IdentityRole>()
    //.AddRoleManager<RoleManager<IdentityRole>>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

I don't know why but AddRoles and AddRoleManager don't support to check role for a user (User.IsInRole).

In this case, we need to register service like this:

services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

By using this way, we create 3 user roles in the databse:

1

When register new user, we just need to call:

await _userManager.AddToRoleAsync(user, nameof(UserRoles.Administrator));

Finally, we can use [Authorize(Roles = "Administrator")] and:

if (User.IsInRole("Administrator"))
{
    // authorized
}

// or
if (User.IsInRole(nameof(UserRoles.Administrator)))
{
    // authorized
}

// but
if (User.IsInRole("ADMINISTRATOR"))
{
    // authorized
}

P/S: There are a lot things which need to be implement to achieve this goal. So maybe I missed something in this example.