ASP.NET Core Identity Series Eight

In this section, we mainly introduce the use of policies for authorization in ASP.NET Core Identity. Policy is a set of collections that users must have to authorize access to resources on the application. The authorization of Identity Policy can include Role and Claim for users, which helps us build a richer authorization structure in the application

For example: We create an Identity Policy named MIT, which contains 3 necessary conditions: “high school grade is A”, “under 18 years old”, “US citizenship”. Now that we apply this strategy to the application area of the MIT portal, only these students can apply to graduate programs:

  • Graduated from high school with an A

  • under 18

  • American citizenship

1. Create an ASP.NET Core Identity policy

We create the Identity policy in the program:

builder.Services.AddAuthorization(authorizationOptions =>
{
    authorizationOptions. AddPolicy("AspManager", authorizationPolicy =>
    {
        authorizationPolicy. RequireRole("Manager");
        authorizationPolicy.RequireClaim("Coding-Skill", "ASP.NET Core MVC");
    });
});

AspManager strategy has two necessary conditions:

  • User role must be Manager

  • User Claim type is Coding-Skill, Claim value is ASP.NET Core MVC

2. Authorization based on ASP.NET Core Identity policy

We can apply the policy we just created to any Controller and Action method, use the policy-based authorization feature in the application, enter the ClaimsController to create a new Project method, and apply the [Authorize(Policy = “AspManager”)] feature To this method, the code is as follows:

[Authorize(Policy = "AspManager")]
public IActionResult Project() => View("Index", User. Claims);

Currently, ASP.NET Core Identity only authorizes the following users to access Project methods:

  • This user belongs to the Manager role.

  • The user has a Claim type of Coding-Skill and a Claim value of ASP.NET Core MVC

The following are the more important methods for creating a Policy:

name

describe

RequireUserName(name)

specify a specific user

RequireClaim(type, value)

The user needs to have the Claim type and the corresponding value (value type Param: params string[] or

Param: IEnumerable)

RequireRole(roles)

User must be a member of the specified role. The parameter types are:

Param: params string[] roles or

Param: IEnumerable roles

AddRequirements(requirement)

specify a custom policy

3. Test Identity policy authorization

Use email: [email protected] password: Coder77@1 to test, first confirm whether pintu belongs to the Manager role, if not, add it, we see that pintu belongs to the Manager role, but there is no Coding-Skill Claim

362560b5581d671e0efb6e655f06ddbc.png

Now let’s try to access the Project method of ClaimsController,

https://localhost:7296/Claims/Project. We see Access Denied:

5c1e1dc123a1babf4cf563ec3417701a.png

So we need to add a Claim whose type is Coding-Skill and value is ASP.NET Core MVC to user pintu. We create a new Claim for pintu, the URL is: https://localhost:7296/Claims/Create (see the picture below)

9be766116d8c47bc4651e0bdfd7f47a7.png

After adding, you need to log out and log in again, we enter the following URL

https//localhost:7296/Claims. We can see the newly added Claim

c73cb3fd10567a2cf590e84f8cb824c0.png

Finally, access the Project method of ClaimsController, through the following URL, https://localhost:7296/Claims/Project, we can access it at this time, and display all the Claims under the pintu user:

310ef74c95422033691c3574373a04f6.png

4. Custom Requirement policy authorization

We create a custom policy that only allows the Tom user to access the controller’s methods. As follows:

  • Create a class that implements the IAuthorizationRequirement interface, which internally provides a mechanism to determine whether authorization succeeds or fails

  • Create a subclass that implements the AuthorizationHandler class to evaluate authorization requirements

We create a CustomPolicy folder in the root directory, and create the AllowUserPolicy.cs class under this folder. The AllowUserPolicy.cs class code is as follows:

public class AllowUserPolicy : IAuthorizationRequirement
{
    public string[] AllowUsers { get; set; }
    public AllowUserPolicy(params string[] allowUsers)
    {
        AllowUsers = allowUsers;
    }
}

The AllowUserPolicy class implements the IAuthorizationRequirement interface and obtains all users who are allowed to access resources through the constructor parameters. Next, we create another class AllowUsersHandler.cs under the CustomPolicy folder. The code is as follows:

public class AllowUsersHandler : AuthorizationHandler<AllowUserPolicy>
{
    public override Task HandleAsync(AuthorizationHandlerContext context)
    {
        return base.HandleAsync(context);
    }
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AllowUserPolicy requirement)
    {
        if (requirement.AllowUsers.Any(user => user.Equals(context.User.Identity?.Name, StringComparison.OrdinalIgnoreCase)))
        {
            context.Succeed(requirement);
        }
        else
        {
            context. Fail();
        }
        return Task.CompletedTask;
    }

AllowUsersHandler authorizes the Handler to inherit the AuthorizationHandler class. When the authorization system needs to check the access Action, the HandleRequirementAsync() method is called. The parameters of this method accept the AuthorizationHandlerContext object and the AllowUserPolicy class. The AllowUserPolicy class provides users who are allowed to access resources. The main members of the AuthorizationHandlerContext class are as follows:

name

describe

Succeed(requirement)

This method is called if the request meets the requirements. The parameter of this method is the AllowUserPolicy object

Fail()

This method is called if the request does not meet the requirements

Resource

This property returns an object that authorizes access to the resource

So, inside the HandleRequirementAsync() method, we check if the currently logged in user is in the list of allowed logged in users, if yes, we call the Succeed method, otherwise we call the fail method

Now we create a Policy and send an allowed user to the AllowUserPolicy class. We use the AddRequirements(requirement) method to add customer-defined policies. The code is as follows:

builder.Services.AddTransient<IAuthorizationHandler,AllowUsersHandler>();
builder.Services.AddAuthorization(authorizationOptions =>
{
    authorizationOptions. AddPolicy("AspManager", authorizationPolicy =>
    {
        authorizationPolicy. RequireRole("Manager");
        authorizationPolicy.RequireClaim("Coding-Skill", "ASP.NET Core MVC");
    });
    authorizationOptions. AddPolicy("AllowTom", authorizationPolicy =>
    {
        authorizationPolicy. AddRequirements(new AllowUserPolicy("tom"));
    });
});

Now, we apply this policy to the Controller method, we add a TomFiles method in ClaimsController and apply this [Authorize(Policy = “AllowTom”)] attribute on this method. Look at the following code:

[Authorize(Policy = "AllowTom")]
public IActionResult TomFiles() => View("Index", User. Claims);

This method can only be called by tom users. If another user calls this method, it will redirect to the rejection page. Use the Tom user to test, enter https://localhost:

7296/Claims/TomFiles54b2abbddc186a9d58690ec4af069536.png

Now, we use [email protected] to log in, https://localhost:7296/Claims/TomFiles, it will jump to the denial page

c86855d3414eca7fcb37f776c661297f.png

5. Do not use [Authorize] to submit a Policy

So far we can use the [Authorize] policy for the Action of Control or Controller, but in some application scenarios we don’t want to use the [Authorize(Policy = “SomePolicy”)] feature to submit the policy, you can use the IAuthorizationService interface to do it, we pass To understand an example, create the AllowPrivatePolicy.cs class in the CustomPolicy directory:

public class AllowPrivatePolicy : IAuthorizationRequirement
{
}
public class AllowPrivateHandler : AuthorizationHandler<AllowPrivatePolicy>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AllowPrivatePolicy requirement)
    {
        string[] allowUsers = context. Resource as string[];
        if(allowUsers.Any(user=>user.Equals(context.User.Identity?.Name,StringComparison.OrdinalIgnoreCase)))
        {
            context.Succeed(requirement);
        }
        else
        {
            context. Fail();
        }
        return Task.CompletedTask;
    }
}

We register the AuthorizationHandler in the application’s Program class and create a new policy called PrivateAccess

builder.Services.AddTransient<IAuthorizationHandler, AllowUsersHandler>();
builder.Services.AddTransient<IAuthorizationHandler,AllowPrivateHandler>();
builder.Services.AddAuthorization(authorizationOptions =>
{
    authorizationOptions. AddPolicy("AspManager", authorizationPolicy =>
    {
        authorizationPolicy. RequireRole("Manager");
        authorizationPolicy.RequireClaim("Coding-Skill", "ASP.NET Core MVC");
    });
    authorizationOptions. AddPolicy("AllowTom", authorizationPolicy =>
    {
        authorizationPolicy. AddRequirements(new AllowUserPolicy("tom"));
    });
    authorizationOptions. AddPolicy("PrivateAccess", authorizationPolicy =>
    {
        authorizationPolicy. AddRequirements(new AllowPrivatePolicy());
    });
});

Note that we did not pass policy.AddRequirements(new AllowPrivate

Policy()) passes any parameters to the AllowPrivatePolicy class, instead we pass the data through the Controller. Finally, add the IAuthorizationService service in the ClaimsController constructor. Add a PrivateAccess method to verify the PrivateAccess we just added. We add a new PrivateAccess method and use authService.AuthorizeAsync to authenticate the PrivateAccess policy we just added

code show as below:

public async Task<IActionResult> PrivateAccess()
{
    string[] allowedUsers = { "tom", "alice" };
    var authorized=await _authorizationService.AuthorizeAsync(User,allowedUsers,"PrivateAccess");
    if(authorized. Succeeded)
    {
        return View("Index", User. Claims);
    }
    else
    {
        return new ChallengeResult();
    }
}

This method can only be called by two users, tom and alice. Note that we provided the allowUsers variable as the second parameter of the AuthorizeAsync() method of AllowPrivateHandler. The following code:

var authorized=await _authorizationService.AuthorizeAsync(User,allowedUsers,"PrivateAccess");

The AllowPrivateHandler class obtains the allowedUsers value through the Resource property of the AuthorizationHandlerContext class, the code is as follows

string[] allowUsers = context.Resource as string[];

Initializing ChallengeResult will force all users except tom and alice to redirect to the login page. Log in with the alice user and we can see that the method is called, enter https://localhost:7296/Claims/PrivateAccess

a2763f5dbf12351b3c49c11b2fcb2476.png

Now we log in with the mary account and enter the same page, we find that we have been redirected to the login page

Summary

In this section we mainly explain about policy authorization

Source code address:

https://github.com/bingbing-gui/Asp.Net-Core-Skill/tree/master/AspNetCore.Identity/Identity