asp.net core series 50 Identity authorization (medium)

1.5 Policy-Based Authorization

In the previous article, we have already talked about the four methods of authorized access (authorization). Among them, Razor Pages authorization agreement and simple authorization are more like authentication, because as long as a legitimate user logs in, he can access resources. The two methods of role authorization and statement authorization are true authorized access (authorization).

Next, we will continue to talk about the fifth method of authorization-strategic authorization. Policy authorization consists of one or more requirements (also called “requirements”) (requirement: TRequirement). It is registered as part of the authorization service configuration when the program starts. Register in the ConfigureServices method.

(1) Registration policy authorization

A policy authorization named “AtLeast21” is created. The requirement of this policy is the minimum age requirement. The policy is provided through the parameter object (IAuthorizationRequirement), which requires the minimum age to be 21 years old.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

                services. AddAuthorization(options =>
                 {
                     options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
                    // MinimumAgeRequirement parameter object implements IAuthorizationRequirement
                     options.AddPolicy("AtLeast21", policy => policy.Requirements.Add(new MinimumAgeRequirement(21)));
                 });
}

(2) Policy authorization is applied to mvc controller or Razor Pages

//User purchase wine business, policy authorization is applied to the controller, requiring the user to be no less than 21 years old
[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseController : Controller
{
    public IActionResult Index() => View();
}
//Policy authorization to the PageModel class of razor pages
[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseModel : PageModel
{
}

In razor pages, policies can also be applied to razor page authorization agreements.

 public static PageConventionCollection AuthorizeFolder(this PageConventionCollection conventions, string folderPath, string policy);

(3) Requirement policy authorization requirements

The policy authorization requirement implements the IAuthorizationRequirement interface, which is used to pass policy requirement object parameters. MinimumAgeRequirement is a requirement parameter object.

using Microsoft.AspNetCore.Authorization;
public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public int MinimumAge { get; }

    public MinimumAgeRequirement(int minimumAge)
    {
        MinimumAge = minimumAge;
    }
}

(4) Policy authorization handler class

The authorization processing program is responsible for evaluating the required attributes (referring to the policy authorization logic processing, which verifies the current user’s age and the age required by the policy). The authorization handler evaluates the request against the provided AuthorizationHandlerContext to determine whether access is allowed or denied.

To implement the policy authorization handler, you need to inherit AuthorizationHandler, where TRequirement is the parameter object. Alternatively, a handler can handle multiple types of requests by implementing IAuthorizationHandler.

The following is an example of a one-to-one relationship (one Handler handles one TRequirement object), evaluating the minimum age requirement:

 public class MinimumAgeHandler: AuthorizationHandler<MinimumAgeRequirement>
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                        MinimumAgeRequirementrequirement)
        {
            if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth & amp; & amp;
                                            c.Issuer == "LOCAL AUTHORITY"))
            {
                //TODO: Use the following if targeting a version of
                //.NET Framework older than 4.6:
                // return Task.FromResult(0);
                return Task.CompletedTask;
            }

            var dateOfBirth = Convert.ToDateTime(
                context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth & amp; & amp;
                                            c.Issuer == "LOCAL AUTHORITY").Value);

            int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
            if (dateOfBirth > DateTime. Today. AddYears(-calculatedAge))
            {
                calculatedAge--;
            }

            if (calculatedAge >= requirement.MinimumAge)
            {
               //Satisfied requirements as its only parameter
                context.Succeed(requirement);
            }

            //TODO: Use the following if targeting a version of
            //.NET Framework older than 4.6:
            // return Task.FromResult(0);
            return Task.CompletedTask;
        }
    }

The above code is to determine whether the current user has a date of birth claim (ClaimTypes.DateOfBirth) issued by a known trusted issuer (Issuer). Authorization is not possible when the current user is missing claims, in which case completed tasks are returned. If a claim is present, the user’s age is calculated. Authorization is considered successful if the user meets the minimum age defined by this requirement. After successful authorization, context.Succeed is called with the satisfied requirements as its only parameter.

(5) The handler is injected into the service collection, using a singleton

 services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();

In the UserClaim user declaration table, save a piece of data that conforms to the policy authorization. When the program is started and the AlcoholPurchase resource is accessed, the authorization handler MinimumAgeHandler is entered. After context.Succeed(requirement) is executed, the authorization is successful.

1.5.1 Use one handler for multiple requests

The following is a handler for multiple requirements (TRequirement). The Handler implements the IAuthorizationHandler interface. The following example is a multi-relationship permission handler, which can handle three different types of requirements:

public class PermissionHandler : IAuthorizationHandler
{
    public Task HandleAsync(AuthorizationHandlerContext context)
    {
         //Get multiple requirements in the strategy, return IEnumerable<IAuthorizationRequirement> type
        var pendingRequirements = context. PendingRequirements. ToList();
         
        foreach (var requirement in pendingRequirements)
        {
            //Read authorization
            if (requirement is ReadPermission)
            {
                if (IsOwner(context.User, context.Resource) ||
                    IsSponsor(context.User, context.Resource))
                {
                    context.Succeed(requirement);
                }
            }
            //Edit and delete authorization
            else if (requirement is EditPermission ||
                     requirement is DeletePermission)
            {
                if (IsOwner(context. User, context. Resource))
                {
                    context.Succeed(requirement);
                }
            }
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        // return Task. FromResult(0);
        return Task.CompletedTask;
    }

For detailed code, see the official example, Github.

1.5.2 What should the handler return? (There are three returns)

(1) The handler signals success by calling context.Succeed(IAuthorizationRequirement requirement) and passing the successfully authenticated requirement.

(2) Handlers generally do not need to handle failures (explicit plus context.Fail() ), since other handlers (1.5.3) for the same requirement may succeed.

(3) To guarantee that authorization fails even if other request handlers would succeed, call context.Fail();

 1.5.3 One requirement applies to multiple handlers

The authorization processing here is exactly the opposite of 15.1. The following example is the access card authorization policy requirement. Your company’s access card is left at home. When you go to the company, you ask the front desk to give you a temporary access card to open the door. In this case, there is only one requirement but multiple handlers, each checking against a single requirement.

 //Policy authorization requirements, there are no parameter requirements here, the parameters are hard-coded in the handler
  ? public class BuildingEntryRequirement: IAuthorizationRequirement
   {
   }
//Access card processing program
public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   BuildingEntryRequirementrequirement)
    {
            //Requirement parameter hard-coded BadgeId
        if (context.User.HasClaim(c => c.Type == "BadgeId" & amp; & amp;
                                       c.Issuer == "http://microsoftsecurity"))
        {
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        // return Task. FromResult(0);
        return Task.CompletedTask;
    }
}
//Temporary access control card processing program
public class TemporaryStickerHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   BuildingEntryRequirementrequirement)
    {
        //Requirement parameter hard-coded TemporaryBadgeId
        if (context.User.HasClaim(c => c.Type == "TemporaryBadgeId" & amp; & amp;
                                       c.Issuer == "https://microsoftsecurity"))
        {
            // We'd also check the expiration date on the sticker.
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        // return Task. FromResult(0);
        return Task.CompletedTask;
    }
}
 // registration strategy
    services.AddAuthorization(options =>
            {
                  options.AddPolicy("BadgeEntry", policy =>policy.Requirements.Add(new BuildingEntryRequirement()));
        });
 // inject service
  services.AddSingleton<IAuthorizationHandler, BadgeEntryHandler>();
   services.AddSingleton<IAuthorizationHandler, TemporaryStickerHandler>();

When [Authorize(Policy = ” BadgeEntry “)] is applied to the controller, as long as one handler succeeds, the policy authorization is successful. The ClaimType needs to be maintained in the UserClaim user claim table.

1.5.4 Use func to satisfy strategy

In some cases, strategies are easy to implement in code. RequireAssertion can be provided when configuring the policy through the Func policy generator. For example, the previous BadgeEntryHandler can be overridden as follows:

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

Summary: Through these two articles, you have become familiar with the five methods of authorization, including:

? Razor Pages Licensing Agreement
? Simple authorization
Role authorization
Declaration of authorization
?Strategic authorization

The statement authorization includes role authorization, and role authorization can be used in the statement through ClaimTypes.Role.

 public const string Role = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"

references

Policy-based authorization