Search code examples
c#asp.net-coreasp.net-identityasp.net-core-identity

User.IsInRole() returns false and Authorize Roles gives me an Access Denied


I'm trying to set up the authentication in my existing App with ASP.NET Core Identity 2.0. As I use my own database schema and classes, I have my own User and Role classes and I had to create custom UserStore/UserManager/RoleStore/RoleManager.

I declare them in my services :

services.AddIdentity<User, Profile().AddUserManager<CustomUserManager<User>>().AddRoleManager<CustomRoleManager>().AddDefaultTokenProviders();
services.AddTransient<IUserStore<User>, UserStore>();
services.AddTransient<IRoleStore<Profile>, ProfileStore>();
services.AddTransient<UserResolverService>();

I implement the interface UserRoleStore in the UserStore with these methods :

    public Task AddToRoleAsync(User user, string roleName, CancellationToken cancellationToken)
    {
        user.Profiles.Add(db.Profiles.ToList().Find(x => x.Name.Equals(roleName, StringComparison.OrdinalIgnoreCase)));

        db.SaveChanges();

        return Task.FromResult((object)null);
    }

    public Task RemoveFromRoleAsync(User user, string roleName, CancellationToken cancellationToken)
    {
        user.Profiles.Remove(db.Profiles.ToList().Find(x => x.Name.Equals(roleName, StringComparison.OrdinalIgnoreCase)));

        db.SaveChanges();

        return Task.FromResult((object)null);
    }

    public Task<IList<string>> GetRolesAsync(User user, CancellationToken cancellationToken)
    {
        IList<string> ret = new List<string>();
        user.Profiles.ToList().ForEach(x => ret.Add(x.Name));
        return Task.FromResult(ret);
    }

    public Task<bool> IsInRoleAsync(Useruser, string roleName, CancellationToken cancellationToken)
    {
        Profile searchRole = db.Profiles.Include(profile => profile.ProfileUsers).ToList().Find(x => x.Active && x.Name.Equals(roleName, StringComparison.OrdinalIgnoreCase));
        return Task.FromResult(searchRole.ProfileUsers.ToList().Find(x => x.UserID == user.ID) == null ? false : true);
    }

    public Task<bool> IsInRole(User user, string roleName, CancellationToken cancellationToken)
    {
        return Task.FromResult(true);
    }

    public Task<IList<Utilisateur>> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken)
    {
        Profile currentProfile = db.Profiles.Include(profile => profile.ProfileUsers).ThenInclude(pu => pu.User).ToList().Find(x => x.Active && x.Name.Equals(roleName, StringComparison.OrdinalIgnoreCase));
        if (currentProfile == null)
        {
            return Task.FromResult((IList<User>)null);
        }

        IList<User> ret = new List<User>();
        currentProfile.ProfileUsers.ToList().ForEach(x => ret.Add(x.User));
        return Task.FromResult(ret);
    }

So when I try to do

var result1 = _userManager.AddToRoleAsync(userObject, "WorkOrderViewer");
var res = _userManager.GetUsersInRoleAsync("WorkOrderViewer");
var test = await _userManager.IsInRoleAsync(userObject, "WorkOrderViewer");

It works and my user gets the choosen Role. However, when I try to set up the controller authorizations or the controls visibility in the view it doesn't work. For the controller authorization I have an access denied for exemple.

When I put this in my code :

var test = User.IsInRole("WorkOrderViewer");

It returns "false" even if I am log in the app (I try to log out and log in) and if the UserManager.IsInRoleAsync returns me "true".

I think it fails because UserManager.AddToRoleAsync() is not synchronized with User.IsInRole() but I don't know how to resolve it. Have you an idea of what I am doing wrong ?


Solution

  • When you say you use the code User.IsInRole I assume you mean in a Controller.

    User in a controller is of type ClaimsPrincipal.

    And the method ClaimsPrincipal.IsInRole has the following documentation:

    The IsInRole method checks whether an identity that this claims principal possesses contains a claim of type ClaimsIdentity.RoleClaimType where the value of the claim is equal to the value specified by the role parameter.

    This can be confirmed by looking at the source for ClaimsIdentity.cs.

    if (_identities[i].HasClaim(_identities[i].RoleClaimType, role))
    

    So User.IsInRoleis checking to see if the User has a Claim of type RoleClaimType (which defaults to "http://schemas.microsoft.com/ws/2008/06/identity/claims/role.").

    The claim type can be configured as needed.