The problem is solved (it seems), I am posting my solution, since I did not find suitable answers, and I think this may be useful to others.
The correct track was found in the answer to the Reuse Claim question in regenerating the IdentityCallback in Owin Identity in MVC5
I just changed the code a bit, since UserId in my case has a type string, not a Guid.
Here is my code:
In Startup.Auth.cs
app.UseCookieAuthentication(new CookieAuthenticationOptions() { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login"), Provider = new CookieAuthenticationProvider { // Enables the application to validate the security stamp when the user logs in. // This is a security feature which is used when you change a password or add an external login to your account. //OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>( // validateInterval: TimeSpan.FromMinutes(1d), // regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager, DefaultAuthenticationTypes.ApplicationCookie)) OnValidateIdentity = context => SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, string>( validateInterval: TimeSpan.FromMinutes(1d), regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager, context.Identity), getUserIdCallback: (ci) => ci.GetUserId()).Invoke(context) }, /// TODO: Expire Time must be reduced in production do 2h //ExpireTimeSpan = TimeSpan.FromDays(100d), ExpireTimeSpan = TimeSpan.FromMinutes(2d), SlidingExpiration = true, CookieName = "RMC.AspNet", });
NOTE Note that in my example, ExpireTimeSpan and validateInterval are ridiculously short, since the goal here was to call the most frequent confirmation for testing purposes.
The IdentityModels.cs is overloaded with GenerateUserIdentityAsync, which takes care of reinstalling all of the user's Identity requirements.
/// Generates user Identity based on Claims already defined for user. /// Used fro Identity re validation !!! /// </summary> /// <param name="manager"></param> /// <param name="CurrentIdentity"></param> /// <returns></returns> public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, string> manager, ClaimsIdentity CurrentIdentity) { // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie); // Re validate existing Claims here userIdentity.AddClaims(CurrentIdentity.Claims); return userIdentity; }
It works. Not sure if this is the best solution, but if anyone has the best approaches, feel free to improve my answer.
Thanks.
Lorenzo
ADDITION
After some time, I found out that what is implemented in GenerateUserIdentityAsync (...) can cause problems if used together with @ Html.AntiForgeryToken (). My previous implementation would continue to add pre-existing claims at each re-certification. This confuses the AntiForgery logic that causes the error. To prevent this, I repeated it as follows:
/// <summary> /// Generates user Identity based on Claims already defined for user. /// Used fro Identity re validation !!! /// </summary> /// <param name="manager"></param> /// <param name="CurrentIdentity"></param> /// <returns></returns> public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, string> manager, ClaimsIdentity CurrentIdentity) { // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie); // Re validate existing Claims here foreach (var Claim in CurrentIdentity.Claims) { if (!userIdentity.HasClaim(Claim.Type, Claim.Value)) userIdentity.AddClaim(new Claim(Claim.Type, Claim.Value)); } return userIdentity; } }
APPENDIX 2
I had to clarify the mechanism because my previous ADDENDUM would lead in some special cases to the same problem as with re-validation. The key to the current final decision is to add claims that I can clearly identify and add only those that were rechecked without having to try to distinguish between betweeb native (ASP Identity) and mine. So, now during LogIn, I add the following user claims:
User.Claims.Add(new ApplicationIdentityUserClaim() { ClaimType = "CustomClaim.CultureUI", ClaimValue = UserProfile.CultureUI }); User.Claims.Add(new ApplicationIdentityUserClaim() { ClaimType = "CustomClaim.CompanyId", ClaimValue = model.CompanyId });
Pay attention to the type of application, which now begins with "CustomClaim.".
Then when re-checking, I do the following:
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, string> manager, ClaimsIdentity CurrentIdentity) { // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie); // Re validate existing Claims here foreach (var Claim in CurrentIdentity.FindAll(i => i.Type.StartsWith("CustomClaim."))) { userIdentity.AddClaim(new Claim(Claim.Type, Claim.Value)); // TODO devo testare perchรฉ va in loop la pagina Err500 per cui provoco volontariamente la duplicazioen delle Claims //userIdentity.AddClaims(CurrentIdentity.Claims); } return userIdentity; }
userIdentity does not contain user claims, while CurrentIdentity contains both, but the only thing I have to โattachโ to the current identifier is my user one.
So far this is working fine, so I will mark this as an answer.
Hope this helps!
Lorenzo