When using Cookie Authentication in ASP.NET Core 2 (with or without Identity) it might happen, that a user's email or name is changed, or even the account is deleted during the cookie's lifetime. That's why the docs point out, that the cookie should be validated. The example in the docs is commented with
The approach described here is triggered on every request. This can result in a large performance penalty for the app.
So I am wondering what is the best pattern to validate the cookie principal. What I did in Startup.cs is to subscribe to the OnValidatePrincipal event and check the pricipal's validity e.g. every 5 minutes by appending a LastValidatedOn claim to the cookie like so:
services.ConfigureApplicationCookie(options =>
{
// other cookie options go here
options.Events.OnValidatePrincipal = async context =>
{
const string claimType = "LastValidatedOn";
const int reValidateAfterMinutes = 5;
if (!(context.Principal?.Identity is ClaimsIdentity claimIdentity)) return;
if (!context.Principal.HasClaim(c => c.Type == claimType) ||
DateTimeOffset.Now.UtcDateTime.Subtract(new DateTime(long.Parse(context.Principal.Claims.First(c => c.Type == claimType).Value))) > TimeSpan.FromMinutes(reValidateAfterMinutes))
{
var mgr = context.HttpContext.RequestServices.GetRequiredService<SignInManager<ApplicationUser>>();
var user = await mgr.UserManager.FindByNameAsync(claimIdentity.Name);
if (user != null && claimIdentity.Claims.FirstOrDefault(c => c.Type == "AspNet.Identity.SecurityStamp")?.Value == await mgr.UserManager.GetSecurityStampAsync(user))
{
claimIdentity.FindAll(claimType).ToList().ForEach(c => claimIdentity.TryRemoveClaim(c));
claimIdentity.AddClaim(new Claim(claimType, DateTimeOffset.Now.UtcDateTime.Ticks.ToString(), typeof(long).ToString()));
context.ShouldRenew = true;
}
else
{
context.RejectPrincipal();
await mgr.SignOutAsync();
}
}
};
});