asp.net core authorization - Operation authorization handler

Viewed 976

I have strange problem and can not debug it.

I am trying to use OperationAuthorizationRequirement and an AuthorizationHandler.

When I place breakpoint in controller, I always get 403 result, so I tried to place breakpoint inside CountryControllerAuthorizationHandler, and it is never triggered. But in controller, I always get result of AuthorizeAsync false but i cannot understand why i cannot get breakpoint being hit or triggered. Am I doing some stupid mistake? Anyway as I can see, resource is NOT null and also User and claims are NOT null:

Here is my code:

CountryControllerAuthorizationHandler:

public class CountryControllerAuthorizationHandler : AuthorizationHandler<OperationAuthorizationRequirement, Country>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   OperationAuthorizationRequirement requirement,
                                                   Country resource)
    {
        if (requirement.Name == Operations.ReadDetail.Name &&
            context.User.Claims.FirstOrDefault(a => a.Type == "userType")?.Value == "customer"
            )
        {
            context.Succeed(requirement);
        }

        if (requirement.Name == Operations.Create.Name &&
            context.User.Claims.FirstOrDefault(a => a.Type == "userType")?.Value == "1"
            )
        {
            context.Succeed(requirement);
        }

        if (requirement.Name == Operations.Update.Name &&
            context.User.Claims.FirstOrDefault(a => a.Type == "userType")?.Value == "1"
            )
        {
            context.Succeed(requirement);
        }

        if (requirement.Name == Operations.Delete.Name &&
           context.User.Claims.FirstOrDefault(a => a.Type == "userType")?.Value == "1"
           )
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

Operations class:

public static class Operations
{
    public static OperationAuthorizationRequirement Create =
        new OperationAuthorizationRequirement { Name = nameof(Create) };
    public static OperationAuthorizationRequirement Read =
        new OperationAuthorizationRequirement { Name = nameof(Read) };
    public static OperationAuthorizationRequirement ReadDetail =
      new OperationAuthorizationRequirement { Name = nameof(ReadDetail) };
    public static OperationAuthorizationRequirement Update =
        new OperationAuthorizationRequirement { Name = nameof(Update) };
    public static OperationAuthorizationRequirement Delete =
        new OperationAuthorizationRequirement { Name = nameof(Delete) };
}

Startup.cs:

services.AddSingleton<IAuthorizationHandler, CountryControllerAuthorizationHandler>();

services.AddMvcCore()
    .AddAuthorization()
    .AddJsonFormatters();

services.AddAuthentication("Bearer")
    .AddIdentityServerAuthentication(options =>
    {
        options.Authority = "http://localhost:5000";
        options.RequireHttpsMetadata = false;
        options.ApiName = "api1";
    });

And finally controller:

public CountriesController(ICountryService service, IAuthorizationService authorizationService)
{
    _authorizationService = authorizationService;
    this.countryService = service;
}

public async Task<object> GetDetail()
{
    var obj = countryService.Get_DETAILS();
    var authorizationResult = await _authorizationService.AuthorizeAsync(User, obj, Operations.ReadDetail);

    if (authorizationResult.Succeeded)
    {
        return Ok(obj);
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}
1 Answers

Make sure countryService.Get_DETAILS() is not null and only return one Country.

For CountryControllerAuthorizationHandler, it accepts Country, so, only one county object is passed will be passed into HandleRequirementAsync.

HandleRequirementAsync is called by HandleAsync in AuthorizationHandler.

Try to make a test with code below to debug the reason HandleRequirementAsync is not called.

    public class CountryControllerAuthorizationHandler : AuthorizationHandler<OperationAuthorizationRequirement, Country>
{
    public override async Task HandleAsync(AuthorizationHandlerContext context)
    {
        if (context.Resource is Country)
        {
            foreach (var req in context.Requirements.OfType<OperationAuthorizationRequirement>())
            {
                await HandleRequirementAsync(context, req, (Country)context.Resource);
            }
        }
    }
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   OperationAuthorizationRequirement requirement,
                                                   Country resource)
    {
        if (requirement.Name == Operations.ReadDetail.Name &&
            context.User.Claims.FirstOrDefault(a => a.Type == "userType")?.Value == "customer"
            )
        {
            context.Succeed(requirement);
        }

        if (requirement.Name == Operations.Create.Name &&
            context.User.Claims.FirstOrDefault(a => a.Type == "userType")?.Value == "1"
            )
        {
            context.Succeed(requirement);
        }

        if (requirement.Name == Operations.Update.Name &&
            context.User.Claims.FirstOrDefault(a => a.Type == "userType")?.Value == "1"
            )
        {
            context.Succeed(requirement);
        }

        if (requirement.Name == Operations.Delete.Name &&
           context.User.Claims.FirstOrDefault(a => a.Type == "userType")?.Value == "1"
           )
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}
Related