Jon Halliday
Jon Halliday

Reputation: 455

ASP.NET Core Identity - UserClaims, UserRoles and RoleClaims

I'm setting up an application using ASP.NET Core Identity, and despite reading thoroughly on the topic, I'm struggling to find the right fit for my requirements.

Users familiar with the subreddit concept of Reddit might understand my requirement well, as the concepts are very similar.

Essentially, I have a requirement where a user can be a moderator of one "area" (similar to a subreddit) and just be a "user" in another "area".

What it boils down to is that when a user logs in, I need to know their roles in every area.

I'm struggling with what this actually means in terms of ASP.NET Core Identity's AspNetUserClaims and AspNetRoles / AspNetUserRoles.

I'm considering defining AspNetRoles like so:

Id     Name
1000   User
2000   Junior Moderator
3000   Senior Moderator

Then setting up AspNetUserClaims like so:

Id     UserId     ClaimType      ClaimValue
1      1          area:public1   1000
2      1          area:private1  2000

This would mean that the user with ID 1 is a "user" of the area "public1" and a junior moderator of the area "private1".

I have a number of problems with this.

First, this breaks first normal form by trying to stuff two values into "ClaimType".

Second, there is no referential integrity in place.

Looking closer at AspNetUserRoles, I see a many-to-many relationship between users and roles. I don't see how this would work unless I define every possible role for every possible area as an AspNetRole. Even then, it's still unclear how I would tie this to AspNetUserClaims in a referentially-secure manner.

To complicate matters, it's unclear whether I just need Claims or a combination of Claims and Roles. The following question touches on the subject, but I'm not seeing a clear path based on my requirements:

Best Practices for Roles vs. Claims in ASP.NET Identity

There's no reason I can't solve the issues I'm experiencing by implementing a solution like the above with claims like "area:public1", but this goes against everything I understand about systems design.

Given the requirements described above, what would be the recommended implementation in ASP.NET Core Identity?

Upvotes: 3

Views: 3300

Answers (1)

Edward
Edward

Reputation: 29976

This would mean that the user with ID 1 is a "user" of the area "public1" and a junior moderator of the area "private1".

If you want to achieve the User with Id 1 has User Role, and the User Role has claims public1 as area type.

You could try code below to configure the data.

    public class HomeController : Controller
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly RoleManager<IdentityRole> _roleManager;
    public HomeController(UserManager<IdentityUser> userManager
        , RoleManager<IdentityRole> roleManager)
    {
        _userManager = userManager;
        _roleManager = roleManager;
    }
    public async Task<IActionResult> Prepare()
    {            
        var userRole = await _roleManager.CreateAsync(new IdentityRole("User"));
        var role = await _roleManager.FindByNameAsync("User");
        var roleClaims = await _roleManager.AddClaimAsync(role, new Claim("area", "public1"));
        var user = await _userManager.FindByNameAsync("UserName");

        var roleToUser = await _userManager.AddToRoleAsync(user, "User");
        return Ok("ok");
    }

Then, get the claims for the user by

    public async Task<IActionResult> Index()
    {
        var user = await _userManager.FindByNameAsync("UserName");
        var roles = await _userManager.GetRolesAsync(user);

        var roleClaims = new List<Claim>();
        foreach (var roleName in roles)
        {
            var role = await _roleManager.FindByNameAsync(roleName);
            var claims = await _roleManager.GetClaimsAsync(role);
            roleClaims.AddRange(claims);
        }

        return View();
    }

For above code, you need to configure .AddRoles<IdentityRole>()

        services.AddDefaultIdentity<IdentityUser>()
            .AddRoles<IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>();

Upvotes: 2

Related Questions