DEV Community

Victor
Victor

Posted on

ASP.NET CORE - How to using IAuthorizationRequirementData

Scenarios may be useful:

  • Using an external service to provide policy evaluation.
  • Using a large range of policies, so it doesn't make sense to add each individual authorization policy with an AuthorizationOptions.AddPolicy call.
  • Creating policies at runtime based on information in an external data source (like a database) or determining authorization requirements dynamically through another mechanism.

Applied: from ASP.NET Core 8

Step 1: Implement a custom Authorize Attribute
In my case, I implement a UserFeatureAuthorizeAttirbute

using Microsoft.AspNetCore.Authorization;
using System;
using System.Collections.Generic;

namespace PracticalAPI.AuthorizationRequirementData
{
    public enum FeatureOperator
    {
        And = 1,
        Or = 2
    }
    public class UserFeatureAuthorizeAttribute : AuthorizeAttribute
        , IAuthorizationRequirement
        , IAuthorizationRequirementData
    {
        internal const string PolicyPrefix = "UserFeature_";
        public FeatureOperator Operator { get; set; }
        public string[] Features { get; set; }
        public UserFeatureAuthorizeAttribute(FeatureOperator featureOperator, params string[] features)
        { 
            Operator = featureOperator;
            Features = features;
        }
        public UserFeatureAuthorizeAttribute(string feature)
        {
            Operator = FeatureOperator.And;
            Features = new string[] { feature };
        }
        public IEnumerable<IAuthorizationRequirement> GetRequirements()
        {
            yield return this;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Implement the AuthorizationHandler

using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging;
using System.Globalization;
using System.Threading.Tasks;
using System;

namespace PracticalAPI.AuthorizationRequirementData
{
    public class UserFeatureAuthorizationHandler : AuthorizationHandler<UserFeatureAuthorizeAttribute>
    {
        private readonly ILogger<UserFeatureAuthorizationHandler> _logger;
        private static string featureType = "feature";

        public UserFeatureAuthorizationHandler(ILogger<UserFeatureAuthorizationHandler> logger)
        {
            _logger = logger;
        }

        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   UserFeatureAuthorizeAttribute requirement)
        {
            if (requirement.Operator == FeatureOperator.And)
            {
                foreach (var feature in requirement.Features)
                {
                    _logger.LogWarning("Evaluating authorization requirement for feature: {Feature}", feature);
                    if (!context.User.HasClaim(featureType, feature))
                    {
                        context.Fail();
                        return Task.CompletedTask;
                    }
                }

                context.Succeed(requirement);
                return Task.CompletedTask;
            }

            foreach (var feature in requirement.Features)
            {
                _logger.LogWarning("Evaluating authorization requirement for feature: {Feature}", feature);
                if (context.User.HasClaim(featureType, feature))
                {
                    context.Succeed(requirement);
                    return Task.CompletedTask;
                }
            }

            context.Fail();
            return Task.CompletedTask;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Register the UserFeatureAuthorizationHandler

using AuthRequirementsData.Authorization;
  using Microsoft.AspNetCore.Authorization;

  var builder = WebApplication.CreateBuilder();
  ....
  // Use Custom Authorization
  // builder.Services.AddSingleton<IAuthorizationHandler, UserFeatureAuthorizationHandler>()

  var app = builder.Build();

  app.MapControllers();

  app.Run();
Enter fullscreen mode Exit fullscreen mode

Step 4: Use the custom authorization attribute in controller

[UserFeatureAuthorize("Feature1")]
    public async Task<ActionResult<bool>> MyAction([FromBody] RequestModel model)
    {
       // Logic code go here
    }
Enter fullscreen mode Exit fullscreen mode

Yup, this is one of many options that you can use to implement a good authorization for your application. You have to figure out which option is fit your scenario.

Happy coding!

Top comments (0)