Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement permission based authorization in ASP.net core Identity?

I am trying to secure my webAPI using asp.net Identity Core. Now I want to create Roles Dynamically and set and remove permission from/to them and in my admin panel.

for example, I have this permissions list:

  • register task
  • assign task
  • change task status
  • verify task status

now I want to create different roles and set this permission to them as my needs and assign these roles to each user.

I searched in UserManager and RoleManager of Identity framework but there was no way to create this functionality.

is there any method for implementing this functionality? I find this useful but this is about dotnet

like image 380
Navid_pdp11 Avatar asked Dec 30 '18 11:12

Navid_pdp11


People also ask

What is permission based authorization?

Role-Based Authorization in ASP.NET Core is a way to restrict/allow users to access specific resources in the application. The [Authorize] attribute when declared in the Controller or any action methods, restricts users bases on his/her role settings.

How would you apply an authorization policy to a controller in an ASP.NET Core application?

Role-Based Authorization in ASP.NET Core You can specify what roles are authorized to access a specific resource by using the [Authorize] attribute. You can even declare them in such a way that the authorization evaluates at the controller level, action level, or even at a global level. Let's take Slack as an example.

How do I Authorize my net core?

Authorization in ASP.NET Core is controlled with AuthorizeAttribute and its various parameters. In its most basic form, applying the [Authorize] attribute to a controller, action, or Razor Page, limits access to that component to authenticated users. Now only authenticated users can access the Logout function.


2 Answers

I find an approach which is using claim and policy for creating a permission-based authorization in this link.

I create a custom claim type such as Application.Permission and then create some classes as following to define my permissions:

public class CustomClaimTypes
{
    public const string Permission = "Application.Permission";
}

public static class UserPermissions
{
    public const string Add = "users.add";
    public const string Edit = "users.edit";
    public const string EditRole = "users.edit.role";
} 

and then I create My roles and then Assign these permissions as claims to the roles with key ApplicationPermission.

await roleManager.CreateAsync(new ApplicationRole("User"));
var userRole = await roleManager.FindByNameAsync("User");
await roleManager.AddClaimAsync(userRole, new Claim(CustomClaimTypes.Permission, Permissions.User.View));    
await roleManager.AddClaimAsync(userRole, new Claim(CustomClaimTypes.Permission, Permissions.Team.View));

in the next step, I add these claims to my token when the user is trying to login to system:

var roles = await _userManager.GetRolesAsync(user);
var userRoles = roles.Select(r => new Claim(ClaimTypes.Role, r)).ToArray();
var userClaims = await _userManager.GetClaimsAsync(user).ConfigureAwait(false);
var roleClaims = await GetRoleClaimsAsync(roles).ConfigureAwait(false);
var claims = new[]
             {
                 new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                 new Claim(ClaimTypes.Email, user.Email),
                 new Claim(ClaimTypes.Name, user.UserName)
             }.Union(userClaims).Union(roleClaims).Union(userRoles);

var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SigningKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

var token = new JwtSecurityToken(
    issuer: _jwtSettings.Issuer,
    audience: _jwtSettings.Audience,
    claims: claims,
    expires: DateTime.UtcNow.AddYears(1),
    signingCredentials: creds);

then I create my policies this way:

public static class PolicyTypes
{
    public static class Users
    {
        public const string Manage = "users.manage.policy";
        public const string EditRole = "users.edit.role.policy";
    }
}

then I set up my authorization service inside startup.cs file in ConfigureServiceSection:

services.AddAuthorization(options =>
{
    options.AddPolicy(PolicyTypes.Users.Manage, policy => { policy.RequireClaim(CustomClaimTypes.Permission, Permissions.Users.Add); });
    options.AddPolicy(PolicyTypes.Users.EditRole, policy => { policy.RequireClaim(CustomClaimTypes.Permission, Permissions.Users.EditRole); });
}

finally, I set policies on my routes and finish:

[Authorize(Policy = PolicyTypes.Users.Manage)]
public async Task<IEnumerable<TeamDto>> GetSubTeams(int parentId)
{
    var teams = await _teamService.GetSubTeamsAsync(parentId);
    return teams;
}
like image 109
Navid_pdp11 Avatar answered Sep 30 '22 15:09

Navid_pdp11


Another solution could be to use funcs to fullfill a policy, see this example taken from https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-3.1:

services.AddAuthorization(options =>
{
     options.AddPolicy("BadgeEntry", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim(c =>
                (c.Type == "BadgeId" ||
                 c.Type == "TemporaryBadgeId") &&
                 c.Issuer == "https://microsoftsecurity")));
});

With this in mind you could make something like the following.

First a class for all of your individual permissions:

public static class PrincipalPermission{

       public static List<Func<AuthorizationHandlerContext, bool>> Criteria = new List<Func<AuthorizationHandlerContext, bool>>
        {
            CanCreateUser
        };

        public static bool CanCreateUser(this AuthorizationHandlerContext ctx)
        {
            return ctx.User.IsInRole(RoleEnum.Admin.ToString());
        }
}

Then this should be added to your configuration:

services.AddAuthorization(options =>
{

    foreach (var criterion in PrincipalPermissions.Criteria)
            {
                options.AddPolicy(criterion.Method.Name, 
                                  policy => policy.RequireAssertion(criterion));
            }  
}

now this can be added to your controller:

    [Authorize(Policy = nameof(PrincipalPermissions.CanCreateUser))]
    public async Task<UserDto> Create([FromBody] CreateUserCommand cmd)
    {
       return await HandleCreateUser(cmd);
    }

By the way, if anyone thinks it is annoying to add the methods to a list of Criteria i am right there with you, and i would appreciate if anyone could come up with a better solution :)

like image 26
Frederik Nygart Avatar answered Sep 30 '22 14:09

Frederik Nygart