Reputation: 1285
I want to log stats for all calls to my .netcore webapi.
I have added a IAsyncActionFilter
for this purpose and it picks up on all the actions.
But I also have Jwt Bearer Authentication enabled and am using the AuthorizeAttribute
on my controller to limit access. When access is denied the Action filter will not be hit.
Whats the best way to add some custom logging (statsd) for authentication in general and failures in particular?
public void ConfigureServices(IServiceCollection services)
{
....
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
// base-address of your identityserver
options.Authority = Configuration["Authority"]; ;
// name of the API resource
options.Audience = "myAudience";
});
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseAuthentication();
...
}
I Notice that JwtBearerOptions
has JwtBearerEvents Events
but I cant get this to work.
Edit : It looks like I am hitting the api with no token at all and the JWT Auth handler returns AuthenticateResult.NoResult() without calling the Events.AuthenticationFailed
Edit 2 : Very frustrating. Looks like the correct place to log would be in Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter
But this is automatically added when you use [Authorize]
and is impossible to override, remove or replace?
Upvotes: 6
Views: 5649
Reputation: 16801
The JwtBearerOptions.cs class exposes an JwtBearerEvents parameter where you can declare your events like this
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
// base-address of your identityserver
options.Authority = Configuration["Authority"]; ;
// name of the API resource
options.Audience = "myAudience";
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
//Log failed authentications
return Task.CompletedTask;
},
OnTokenValidated = context =>
{
//Log successful authentications
return Task.CompletedTask;
}
};
});
Upvotes: 7
Reputation: 1325
I don´t have any experience with Jwt Bearer Authentication - but we´ve had a similar situation. We´ve ended up with a new BaseController where we´re able to Log basic user information and distinguish between [AllowAnonymous]
and [Authorize]
:
public class NewBaseController : Controller
{
protected UserManager<MyUser> UserManager;
protected MyUser CurrentUser;
protected ILogger Logger;
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
UserManager = context.HttpContext.RequestServices.GetService<UserManager<MyUser>>();
var loggerFactory = context.HttpContext.RequestServices.GetService<ILoggerFactory>();
Logger = loggerFactory.CreateLogger(GetType());
// Check if Action is annotated with [AllowAnonymous]
var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
var anonymousAllowed = controllerActionDescriptor.MethodInfo
.GetCustomAttributes(inherit: true)
.Any(a => a.GetType().Equals(typeof(AllowAnonymousAttribute)));
if (!anonymousAllowed)
{
ApplicationUser = UserManager.GetUserAsync(User).Result;
if (ApplicationUser == null)
// do some stuff
Logger.LogInformation("User is {UserId}", CurrentUser.Id);
}
else
{
Logger.LogInformation("User is {User}", anonymous);
}
}
}
Bonus: Every Controller that derives from this base already has an instance of UserManager, ILogger and CurrentUser.
Hope that comes close to what you´re looking for...
Upvotes: 0