Using Microsoft.AspNetCore.Identity
(.net 7 at time of writing), the default security stamp validation is not done anymore if I use a custom CookieAuthenticationEvents
. Configuring the SecurityStampValidatorOptions
has no effect either.
Why is this happening and what can be done to enable the security stamp validation?
program.cs
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.ConfigureApplicationCookie(options =>
options.EventsType = typeof(CustomCookieAuthenticationEvents)
);//this prevents securityStampValidation from occurring.
//set a small interval between validations so we can debug
builder.Services.Configure<SecurityStampValidatorOptions>(o => o.ValidationInterval = TimeSpan.FromSeconds(10));
builder.Services.Configure<SecurityStampValidatorOptions>(o => o.OnRefreshingPrincipal = c =>
{
//breakpoint here is hit if ConfigureApplicationCookie(options.EventsType) is NOT set
return Task.FromResult(0);
});
builder.Services.AddScoped<CustomCookieAuthenticationEvents>();
CustomCookieAuthenticationEvents.cs is an empty class for now
public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{ }
This behavior should be documented as it falls between two products.
The doc says
Events: The Provider may be assigned to an instance of an object created by the application at startup time. The handler calls methods on the provider which give the application control at certain points where processing is occurring. If it is not provided a default instance is supplied which does nothing when the methods are called.
EventsType: If set, will be used as the service type to get the Events instance instead of the property.
Fine, but let's not be misleaded by the events
definition, as it is part of the namespace Microsoft.AspNetCore.Authentication.Cookies
which is not the Identity
namespace.
Instead, let's look at the code for IdentityServiceCollectionExtensions
, which, among other things, does for AddIdentity
.AddCookie(IdentityConstants.ApplicationScheme, o =>
{
o.LoginPath = new PathString("/Account/Login");
o.Events = new CookieAuthenticationEvents
{
OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
};
})
So the cookie Events
is set when adding Identity
, and when we add our custom options.EventsType = typeof(CustomCookieAuthenticationEvents)
, the Events
property is dismissed.
By looking at this code, we see that OnValidatePrincipal
is the only event that is set, so we shouldn't have other unexpected missing functionalities. We also see that a static class is used to call the validation, so we can copy that into our CustomCookieAuthenticationEvents
.
public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{
public async override Task ValidatePrincipal(CookieValidatePrincipalContext context)
{
await SecurityStampValidator.ValidatePrincipalAsync(context);
//optional custom code
}
}