7

I've created a user and attached to him a role that has a number of claims. The problem is I don't see a direct way to access retrieve them using Entity Framework Core and Identity integration. Here's what I'd like to do ideally:

return _context.Users
  .Include(u => u.Roles)
  .ThenInclude(r => r.Role)
  .ThenInclude(r => r.Claims)

But there's not Role property, just RoleId. So I can not Include role claims. Of course I get make a separate query to get claims or even use RoleManager:

var user = _context.Users.Single(x => x.Id == ...);
var role = _roleManager.Roles.Single(x => x.Id ==  user.Roles.ElementAt(0).RoleId);
var claims = _roleManager.GetClaimsAsync(role).Result;

but it looks inefficient and even ugly. There should be a way to make a single query.

My last hope was Controller.User property (ClaimsIdentity). I hoped it somehow smartly aggregates claims from all the roles. But seems like it doesn't...

SiberianGuy
  • 24,674
  • 56
  • 152
  • 266
  • Controller.User should work. That's what I use to check the currently logged in session. Are you checking the logged in session, or are you trying to lookup some other user? – Mike_G Mar 02 '17 at 21:50
  • I'm trying to get it for the logged in user. I also thought it should work but it doesn't seem to be the case. Seems like it get only User.Claims. – SiberianGuy Mar 03 '17 at 04:59
  • @Mike_G, it seems like the reason is I use IdentityServer and it doesn't take care of Role Claims by default. May be if I used other authentication approach, it would retrieve Role Claims by default. – SiberianGuy Mar 03 '17 at 07:16
  • Is there no way to do something like User.HasRoleClaim("ClaimName") where this returns a boolean true if any role the user is in has that claim and false there they don't ? – djack109 Nov 02 '19 at 09:30

3 Answers3

2

You can use SQL-like query expressions and get all claims from all roles of a user like this:

var claims = from ur in _context.UserRoles
             where ur.UserId == "user_id"
             join r in _context.Roles on ur.RoleId equals r.Id
             join rc in _context.RoleClaims on r.Id equals rc.RoleId
             select rc;
tmg
  • 19,895
  • 5
  • 72
  • 76
2

You can add navigation properties.

 public class Role : IdentityRole
 {
    public virtual ICollection<RoleClaim> RoleClaims { get; set; }       
 }

  public class RoleClaim : IdentityRoleClaim<string>
  {
     public virtual Role Role { get; set; }
  }

Then you have to configure your identity db context:

 public class MyIdentityDbContext : IdentityDbContext<User, Role, string, IdentityUserClaim<string>, IdentityUserRole<string>, IdentityUserLogin<string>, RoleClaim, IdentityUserToken<string>>

Usage:

await _context.Roles.Include(r => r.RoleClaims).ToListAsync();

At the end it generates the following query:

SELECT `r`.`Id`, `r`.`ConcurrencyStamp`, `r`.`Name`, `r`.`NormalizedName`, `r0`.`Id`, `r0`.`ClaimType`, `r0`.`ClaimValue`, `r0`.`RoleId`
  FROM `roles` AS `r`
  LEFT JOIN `role_claims` AS `r0` ON `r`.`Id` = `r0`.`RoleId`
  ORDER BY `r`.`Id`, `r0`.`Id`

Source: Identity model customization in ASP.NET Core

chunk1ty
  • 400
  • 3
  • 14
-3
  1. Make sure you are adding the roles and claims correctly. Below is an example of how I create a user and add claims and roles.

    private async Task<IdentityResult> CreateNewUser(ApplicationUser user, string password = null){
    
        //_roleManger is of type RoleManager<IdentityRole>
        // _userManger is of type UserManager<ApplicationUser>
        //and both are injected in to the controller. 
    
        if (!await _roleManger.RoleExistsAsync("SomeRole")){
            await _roleManger.CreateAsync(new IdentityRole("SomeRole"));
        }
    
        var result = password != null ? await _userManager.CreateAsync(user, password) : await _userManager.CreateAsync(user);
    
        if(result.Succeeded) {
    
            await _userManager.AddToRoleAsync(user, "SomeRole");
            await _userManager.AddClaimAsync(user, new Claim(ClaimTypes.Name, user.Email));
    
        }
    
        return result;
    }
    
  2. Then you can use the _userManager to get the claims. This is how I get the current user using _userManager. Then you can just call something like this:

    var claims = await _userManager.GetClaimsAsync(user);
    
Community
  • 1
  • 1
Mike_G
  • 16,237
  • 14
  • 70
  • 101
  • This adds a claim to the user, not role. – LHolleman Jul 25 '17 at 11:20
  • @LHolleman Did you miss the whole "_userManager.AddToRoleAsync" and "CreateRole" lines? Did you read the answer at all? – Mike_G Jul 25 '17 at 12:59
  • 1
    @Mike_G The following line in your answer adds the claim to the user, not to the role. You have added the user to the role but the claim is attached directly to the user and the question is about how to efficiently retrieve claims linked to roles. – Jamie Ide Jan 28 '19 at 21:01