Reputation: 635
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:
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
Upvotes: 2
Views: 3835
Reputation: 29976
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.
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;
}
}
Replace built-in DefaultAuthorizationService
services.AddAuthorization(config =>
{
config.AddPolicy("T1", policy => policy.AddRequirements(new MinimumAgeRequirement(21)));
});
services.Replace(ServiceDescriptor.Transient<IAuthorizationService, CustomAuthorizationService>());
Upvotes: 6
Reputation: 635
Microsoft are implementing this by default in .NET 5.0, see the related GitHub issue for details and links to PR.
https://github.com/aspnet/AspNetCore/issues/7789
Upvotes: 2
Reputation: 30545
you can handle and log this inside Middlewares
public class AuthHandlerMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ErrorHandlingMiddleware> _logger;
public AuthHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, IHostingEnvironment env /* other scoped dependencies */)
{
await _next(context);
if (context.Response.StatusCode == 401)
_logger.LogInformation($"'{context.User.Identity.Name}' is unauthorized");
}
}
In your starup config,
public void Configure(IApplicationBuilder app, ... )
{
....
app.UseMiddleware<AuthHandlerMiddleware>();
}
Upvotes: 0