5

J'applique l'application Asp.NET MVC avec le modèle Identity 2.x Authentication and Authorization. Pendant le processus d'ouverture de session, j'ajoute des revendications personnalisées (qui ne sont pas conservées dans la base de données!) Dérivant des données transmises dans l'identifiant, et je peux y accéder plus tard, jusqu'à ce que l'identité soit régénérée.Revendications personnalisées perdues lors de la validation de l'identité

[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 })); 

     } 

    } 

Le processus de validation est défini dans le OnValidationIdentity que je havn't fait beaucoup à personnaliser. Lorsque validationInterval passe (... ou mieux à mi-chemin au validationInterval) L'identité est générée et les revendications personnalisées sont perdues.

 // 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", 
     }); 

Je pense que je devrais certains comment pouvoir passer les revendications en cours à la GenerateUserIdentityAsync afin que je puisse ajouter à nouveau sur mesure CLIMS, mais je ne sais pas comment.

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; 
    } 

Toute aide est appréciée.

Merci

+0

Allez? Personne? –

Répondre

7

Problème résolu (ce seemms), je poste ma solution depuis que je havn't trouvé peut affecter des réponses et je pense qu'il pourrait être utile aux autres.

La bonne voie a été trouvée dans une réponse à la question Reuse Claim in regenerateIdentityCallback in Owin Identity in MVC5

Je viens de modifier un peu le code depuis le UserId dans mon cas est de type chaîne et non Guid.

Voici mon code:

Dans 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: S'il vous plaît noter que dans mon échantillon ExpireTimeSpan et validateInterval sont ridiculement court depuis le but ici était de provoquer la re la plus fréquente à des fins de test. Dans IdentityModels.cs va la surcharge de GenerateUserIdentityAsync qui prend soin de rattacher toutes les revendications personnalisées à l'identité.

/// 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; 
    } 

Cela fonctionne. Pas vraiment sûr si c'est la meilleure solution, mais au cas où quelqu'un aurait de meilleures approches, n'hésitez pas à améliorer ma réponse.

Merci.

Lorenzo

ADDENDA

Après un certain temps de l'utiliser, j'ai découvert que ce qui a mis en œuvre dans GenerateUserIdentityAsync (...) pourrait donner des problèmes si elle est utilisée en conjonction avec @ Html.AntiForgeryToken (.Ma précédente implémentation continuerait à ajouter des revendications déjà existantes à chaque revalidation. Ceci désoriente la logique d'AntiForgery qui jette l'erreur. Pour éviter que je l'ai re implemnted cette façon:

/// <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; 
    } 

} 

ADDENDA 2

je devais affiner le mécanisme moi parce que mon ADDENDA previosu conduirait dans certains cas particuliers à même problème décrit au cours de re -validation. La clé de la solution définitive actuelle est d'ajouter que les revendications que je peux clairement identifier et ajouter que les cours de revalidation, sans avoir à essayer de distinguer betweeb les natifs (ASP identité) et le mien. Alors maintenant, en vous connectant j'ajouter les revendications personnalisées suivantes: «CustomClaim »

User.Claims.Add(new ApplicationIdentityUserClaim() { ClaimType = "CustomClaim.CultureUI", ClaimValue = UserProfile.CultureUI }); 
User.Claims.Add(new ApplicationIdentityUserClaim() { ClaimType = "CustomClaim.CompanyId", ClaimValue = model.CompanyId }); 

Notez le type de revendication qui commence maintenant avec.

Puis, en revalidation je fais ce qui suit:

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 ne contient pas les revendications sur mesure, alors que CurrentIdentity ne contient à la fois, mais le seul que je dois « re attacher » à l'identité actuelle sont mon coutume.

Jusqu'à présent, il fonctionne très bien, donc je vais marquer cela comme Teh réponse.

Hope it helps!

Lorenzo