14

I am identity 2.1.2 with asp.net core 2.0, I have application claim table which have claim type and claim value i.e Assets ,Assets Edit,Assets, Assets View, where claim types are same with distinct claim values and I am creating policies using claim type name which is working fine for me no clue about how to add multiple policies in one action. Below code is being used in startup file to create policies.

services.AddAuthorization(options =>
{
   var dbContext = SqlServerDbContextOptionsExtensions.UseSqlServer(new DbContextOptionsBuilder<MyDBContext>(),
      Configuration.GetConnectionString("TestIdentityClaimAuth")).Options;

   var dbCon = new MyDBContext(dbContext);
   //Getting the list of application claims.
   var applicationClaims = dbCon.ApplicationClaims.ToList();
   var strClaimValues = string.Empty;
   List<ClaimVM> lstClaimTypeVM = new List<ClaimVM>();
   IEnumerable<string> lstClaimValueVM = null;// new IEnumerable<string>();

   lstClaimTypeVM = (from dbAppClaim
         in dbCon.ApplicationClaims
      select new ClaimVM
      {
         ClaimType = dbAppClaim.ClaimType
      }).Distinct().ToList();

   foreach (ClaimVM objClaimType in lstClaimTypeVM)
   {
      lstClaimValueVM = (from dbClaimValues in dbCon.ApplicationClaims
         where dbClaimValues.ClaimType == objClaimType.ClaimType
         select dbClaimValues.ClaimValue).ToList();

      options.AddPolicy(objClaimType.ClaimType, policy => policy.RequireClaim(objClaimType.ClaimType, lstClaimValueVM));
      lstClaimValueVM = null;
   }
});

And in my controller using the Autherize attribute like this.

[Authorize(Policy = "Assets Edit")]

Please shade some light on it thanks in advance.

Shahar Shokrani
  • 7,598
  • 9
  • 48
  • 91
Alok Binwal
  • 153
  • 1
  • 1
  • 5
  • I think you'd better read the docs: https://learn.microsoft.com/nl-nl/aspnet/core/security/authorization/policies?view=aspnetcore-2.1 Instead of adding all kinds of policies use Authorization Handlers instead. –  Oct 04 '18 at 16:55

3 Answers3

18

For multiple policys, you could implement your own AuthorizeAttribute.

  • MultiplePolicysAuthorizeAttribute

    public class MultiplePolicysAuthorizeAttribute : TypeFilterAttribute
    {
         public MultiplePolicysAuthorizeAttribute(string policys, bool isAnd = false) : base(typeof(MultiplePolicysAuthorizeFilter))
         {
             Arguments = new object[] { policys, isAnd };
         }
    }
    
  • MultiplePolicysAuthorizeFilter

    public class MultiplePolicysAuthorizeFilter : IAsyncAuthorizationFilter
    {
        private readonly IAuthorizationService _authorization;
        public string Policys { get; private set; }
        public bool IsAnd { get; private set; }
    
        public MultiplePolicysAuthorizeFilter(string policys, bool isAnd, IAuthorizationService authorization)
        {
           Policys = policys;
           IsAnd = isAnd;
           _authorization = authorization;
        }
    
        public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            var policys = Policys.Split(";").ToList();
            if (IsAnd)
            {
                foreach (var policy in policys)
                {
                    var authorized = await _authorization.AuthorizeAsync(context.HttpContext.User, policy);
                    if (!authorized.Succeeded)
                    {
                        context.Result = new ForbidResult();
                        return;
                    }
    
                }
             }
             else
             {
                foreach (var policy in policys)
                {
                     var authorized = await _authorization.AuthorizeAsync(context.HttpContext.User, policy);
                     if (authorized.Succeeded)
                     {
                         return;
                     }
    
                }
                context.Result = new ForbidResult();
                return;
            }
         }
    }
    
  • only require one of the policy

    [MultiplePolicysAuthorize("Assets View;Assets Edit;Assets Delete")]
    
  • only require all the policys

    [MultiplePolicysAuthorize("Assets View;Assets Edit;Assets Delete", true)]
    
ahmeticat
  • 1,899
  • 1
  • 13
  • 28
Edward
  • 28,296
  • 11
  • 76
  • 121
  • 3
    I just think Microsoft forgot to implement it. It should be there the same as Roles work. – Herman Van Der Blom Feb 03 '21 at 12:51
  • A similar solution can be found in https://stackoverflow.com/questions/51443605/how-to-include-multiple-policies/ and https://stackoverflow.com/questions/58276650/authorize-against-a-list-of-policies – Terence Golla Sep 24 '21 at 21:16
  • 1
    In above example AuthenticationHandler is not executed. Therefore in IAsyncAuthorizationFilter I am getting User Identity as Not authenticated. Am I missing something. – Kamran Asim Nov 09 '21 at 05:36
  • Thanks, how to add AuthenticationSchemes within this implementation? – Neel Jul 01 '22 at 14:03
  • 1
    Same as @Kaman Asim, it seems like Context.HttpContext.Claims or .User is empty. However, if we navigate to the method where this filter is used, the HttpContext there is properly populated and all claims are present. How can we fix this? – Medismal Sep 06 '22 at 20:39
  • Getting empty claims similar to @Medismal, any solution to this? – Rukshán Dikovita Nov 18 '22 at 09:17
11

If you simply want to apply multiple policies, you can do this:

[Authorize(Policy = "Asset")]
[Authorize(Policy = "Edit")]
public class MyController : Controller {

}

EDIT: to clarify, this is additive - you must pass both policy requirements.

Learner
  • 3,297
  • 4
  • 37
  • 62
herostwist
  • 3,778
  • 1
  • 26
  • 34
  • 7
    I think it would be worth mentioning if this will be interpreted as an AND or an OR .. Probably is an AND (from official docs using `[Authorize(Role=..)]` ) ? – Learner Oct 29 '20 at 09:38
  • 5
    yes to clarify, this is additive - you must pass both policy requirements. – herostwist Oct 29 '20 at 10:14
4

You can use make multiple requirements class implementing IAuthorizationRequirement, and register to the DI container the multiple requirements handlers of AuthorizationHandler.

So you can simply add them to your Policy using the AddRequirement inside AuthorizationPolicyBuilder

public AuthorizationPolicyBuilder AddRequirements(params IAuthorizationRequirement[] requirements);

Startup.cs:

services.AddScoped<IAuthorizationHandler, FooHandler>();
services.AddScoped<IAuthorizationHandler, BooHandler>();

services.AddAuthorization(authorizationOptions =>
{
    authorizationOptions.AddPolicy(
        "FooAndBooPolicy",
        policyBuilder =>
        {
            policyBuilder.RequireAuthenticatedUser();
            policyBuilder.AddRequirements(new FooRequirement(), new BooRequirement());
        });
});

Requirements.cs:

public class FooRequirement : IAuthorizationRequirement { }    
public class FooHandler : AuthorizationHandler<FooRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationContext context, FooRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "Foo" && c.Value == true))
        {
            context.Succeed(requirement);
            return Task.FromResult(0);
        }
    }
}

public class BooRequirement : IAuthorizationRequirement { }    
public class BooHandler : AuthorizationHandler<BooRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationContext context, BooRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "Boo" && c.Value == true))
        {
            context.Succeed(requirement);
            return Task.FromResult(0);
        }
    }
}
Shahar Shokrani
  • 7,598
  • 9
  • 48
  • 91