0

I've got an identity server setup with the following 'look a like' configuration:

        return new List<Client>
        {
            new Client
            {
                [...]
                AllowedGrantTypes = GrantTypes.Implicit,
                [....]
            },
            new Client
            {
                [...]
                AllowedGrantTypes = GrantTypes.ClientCredentials,
                [....]
            }
        };

and controlles annotated like this:

    [Route("api/forms")]
    [ApiController]
    [Authorize(Policy = "user.api.portfolio.manager")]
    [Authorize(Policy = "application.api.portfolio.manager")]
   public class FormsController : ControllerBase
   {
      [...]
   }

and a policy

        private System.Action<AuthorizationOptions> AddJwtAuthorizationPolicyForRole()
        {
            return options => { options.AddPolicy("**POLICY_FOR_GRANT_IMPLICIT**", policy => { 
                policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme); 
                policy.RequireAuthenticatedUser();
                policy.RequireClaim(ClaimTypes.Role, "USER_ACCESSIBLE");
            }); 
            };
        }

        private System.Action<AuthorizationOptions> AddJwtAuthorizationPolicyForRole()
        {
            return options => { options.AddPolicy("**POLICY_FOR_CLIENT_CREDENTIALS**", policy => { 
                policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme); 
                policy.RequireAuthenticatedUser();
            }); 
            };
        }

so I want to achieve:

Clients using the GrantType.ClientCredentials can access the controller without any further needs. Clients using the Implicit Schema must have role USER_ACCESSIBLE

If it's configured like shown above, both policies must apply -> Both grant types are failing.

How can I achieve the described behavior using IdentityServer, that each grant-types may have an independent policy so be applied?

Thanks in advance for your help.

user3066027
  • 317
  • 2
  • 4
  • 15

1 Answers1

1

The most simplest solution is adding another single policy for Implicit + ClientCredential to implement logics for OR conditions .

Or you can create a custom attribute like :

  • 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;
            }
         }
    }
    

If actions require matching one of the policies(OR):

[MultiplePolicysAuthorize("POLICY_FOR_GRANT_IMPLICIT;POLICY_FOR_CLIENT_CREDENTIALS")]

If actions require matching all the policies(And) :

[MultiplePolicysAuthorize("POLICY_FOR_GRANT_IMPLICIT;POLICY_FOR_CLIENT_CREDENTIALS",true)]

Code sample reference : https://stackoverflow.com/a/52639938/5751404

Nan Yu
  • 26,101
  • 9
  • 68
  • 148
  • Thank you very much for your help! It seems to be working. But isn't there any built-in way to achieve this behavior - as my problem seems like a quite common one. – user3066027 Mar 05 '20 at 07:58
  • No , there is no built-in options to do that , or you can use multi handler in one requirement : https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-3.1#why-would-i-want-multiple-handlers-for-a-requirement – Nan Yu Mar 05 '20 at 08:09
  • hmm, cannot get the MultiplePolicyAuthorize implementation to work, my user object is not (yet?) set during the call - is there any other step to take? – user3066027 Mar 05 '20 at 09:56
  • No other steps and confirm 1. you correctly set the polices 2. correctly use AddJwtBearer extension to validate token and create user princple 3. set app.UseAuthentication(); app.UseAuthorization(); in Configure method – Nan Yu Mar 05 '20 at 10:04