Reclaimed User Claims

I am implementing an ASP.NET MVC application using the Identity 2.x authentication and authorization model.

During the LogIn process, I add user claims (they are not stored in the database!), Receiving the data transferred to Login from to Identity, and I can correctly contact them later until the identification is restored.

[HttpPost] [AllowAnonymous] [ValidateHeaderAntiForgeryToken] [ActionName("LogIn")] public async Task<JsonResult> Login(LoginViewModel model, string returnUrl) { if (!ModelState.IsValid) return Json(GenericResponseViewModel.Failure(ModelState.GetErrors("Inavlid model", true))); using (var AppLayer = new ApplicationLayer(new ApplicationDbContext(), System.Web.HttpContext.Current)) { GenericResponseViewModel LogInResult = AppLayer.Users.ValidateLogInCredential(ref model); if (!LogInResult.Status) { WebApiApplication.ApplicationLogger.ExtWarn((int)Event.ACC_LOGIN_FAILURE, string.Join(", ", LogInResult.Msg)); return Json(LogInResult); } ApplicationUser User = (ApplicationUser)LogInResult.ObjResult; // In case of positive login I reset the failed login attempts count if (UserManager.SupportsUserLockout && UserManager.GetAccessFailedCount(User.Id) > 0) UserManager.ResetAccessFailedCount(User.Id); //// Add profile claims for LogIn User.Claims.Add(new ApplicationIdentityUserClaim() { ClaimType = "Culture", ClaimValue = model.Culture }); User.Claims.Add(new ApplicationIdentityUserClaim() { ClaimType = "CompanyId", ClaimValue = model.CompanyId }); ClaimsIdentity Identity = await User.GenerateUserIdentityAsync(UserManager, DefaultAuthenticationTypes.ApplicationCookie); AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = true }, Identity); WebApiApplication.ApplicationLogger.ExtInfo((int)Event.ACC_LOGIN_SUCCESS, "LogIn success", new { UserName = User.UserName, CompanyId = model.CompanyId, Culture = model.Culture }); return Json(GenericResponseViewModel.SuccessObj(new { ReturnUrl = returnUrl })); } } 

The verification process is defined in OnValidationIdentity , which I did not do much for customization. When validationInterval passes (... or is it better said that half the way to validationInterval ), Identity gets re generatd and user claims are lost.

  // Enable the application to use a cookie to store information for the signed in user // and to use a cookie to temporarily store information about a user logging in with a third party login provider 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)) }, /// TODO: Expire Time must be reduced in production do 2h ExpireTimeSpan = TimeSpan.FromDays(100d), SlidingExpiration = true, CookieName = "RMC.AspNet", }); 

I think I need to somehow transfer the current claims to GenerateUserIdentityAsync so that I can add custom clicks, but I don't know how to do this.

 public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, string> manager, string authenticationType) { // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType var userIdentity = await manager.CreateIdentityAsync(this, authenticationType); // Add custom user claims here // ???????????????????????????? return userIdentity; } 

Any help is appreciated.

thanks

+5
source share
1 answer

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

+7
source

Source: https://habr.com/ru/post/1257798/


All Articles