Search code examples
asp.net-core.net-coreasp.net-authorization

How can I see which AuthorizeAttribute Failed ASP.NET Core


I am trying to work out if there is an easy way to get ASP.NET Core to log which [Authorize] attribute is failing. I have a mixture of "Role" and "Policy" authorize attributes but whenever a single one fails the logs just show:

enter image description here

Obviously this is the correct behaviour and it doesn't let someone in with incorrect permissions however if you have multiple attributes it's a bit of a pain to have to go and work out which one failed. If the log simply showed Authorization failed for Policy X then that would be really easy to then find what's failing.

Does anyone know if it's currently possible to make this happen through some option I'm unaware of?

EDIT: For example: If I had [Authorize(Policy = "Policy 1")] and [Authorize(Policy = "Policy 2")] and only "Policy 2" failed. I would like to see something that tells me that it was "Policy 2" that failed.

EDIT: For anyone still coming across this question this has now been implemented by Microsoft and is part of .NET 5.0, see issue https://github.com/aspnet/AspNetCore/issues/7789


Solution

  • For Roles and Policy, they are translated to requirements like RolesAuthorizationRequirement or your custom requirement like MinimumAgeRequirement.

    For Authorization failed., this is logged by DefaultAuthorizationService in AuthorizeAsync, you may not able to get the exact name like Policy 1 and Policy 2. You could get the requirements for Policy.

    Try to check whether workaround below meets your requirement.

    1. Implement custom DefaultAuthorizationService

      public class CustomAuthorizationService : DefaultAuthorizationService, IAuthorizationService
      {
          private readonly AuthorizationOptions _options;
          private readonly IAuthorizationHandlerContextFactory _contextFactory;
          private readonly IAuthorizationHandlerProvider _handlers;
          private readonly IAuthorizationEvaluator _evaluator;
          private readonly IAuthorizationPolicyProvider _policyProvider;
          private readonly ILogger _logger;
      
          public CustomAuthorizationService(IAuthorizationPolicyProvider policyProvider
              , IAuthorizationHandlerProvider handlers
              , ILogger<DefaultAuthorizationService> logger
              , IAuthorizationHandlerContextFactory contextFactory
              , IAuthorizationEvaluator evaluator
              , IOptions<AuthorizationOptions> options) 
              : base(policyProvider, handlers, logger, contextFactory, evaluator, options)
          {
              _options = options.Value;
              _handlers = handlers;
              _policyProvider = policyProvider;
              _logger = logger;
              _evaluator = evaluator;
              _contextFactory = contextFactory;
          }
      
          public new async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements)
          {
              if (requirements == null)
              {
                  throw new ArgumentNullException(nameof(requirements));
              }
      
              var authContext = _contextFactory.CreateContext(requirements, user, resource);
              var handlers = await _handlers.GetHandlersAsync(authContext);
              foreach (var handler in handlers)
              {
                  await handler.HandleAsync(authContext);
                  if (!_options.InvokeHandlersAfterFailure && authContext.HasFailed)
                  {
                      break;
                  }
              }
      
              var result = _evaluator.Evaluate(authContext);
              if (result.Succeeded)
              {
                  _logger.LogInformation($"Authorization is succeeded for { JsonConvert.SerializeObject(requirements) }" );
                  //_logger.UserAuthorizationSucceeded();
              }
              else
              {
                  //var r = result.Failure.FailedRequirements.Select(requirement => new { Requirement = requirement.GetType() });
                  var json = JsonConvert.SerializeObject(result.Failure.FailedRequirements);
                  _logger.LogInformation($"Authorization is failed for { json }");
                  //_logger.UserAuthorizationFailed();
              }
              return result;
          }
      
      }
      
    2. Replace built-in DefaultAuthorizationService

      services.AddAuthorization(config =>
      {
          config.AddPolicy("T1", policy => policy.AddRequirements(new MinimumAgeRequirement(21)));
      });
      
      services.Replace(ServiceDescriptor.Transient<IAuthorizationService, CustomAuthorizationService>());