¿Cómo actualizar un reclamo en ASP.NET Identity?

Estoy usando autenticación OWIN para mi proyecto MVC5. Este es mi SignInAsync

  private async Task SignInAsync(ApplicationUser user, bool isPersistent) { var AccountNo = "101"; AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); identity.AddClaim(new Claim(ClaimTypes.UserData, AccountNo)); AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent, RedirectUri="Account/Index"}, identity); } 

Como puede ver, agregué AccountNo en la lista de Reclamaciones.

Ahora, ¿cómo puedo actualizar este reclamo en algún momento de mi solicitud? Hasta ahora, tengo esto:

  public string AccountNo { get { var CP = ClaimsPrincipal.Current.Identities.First(); var Account= CP.Claims.FirstOrDefault(p => p.Type == ClaimTypes.UserData); return Account.Value; } set { var CP = ClaimsPrincipal.Current.Identities.First(); var AccountNo= CP.Claims.FirstOrDefault(p => p.Type == ClaimTypes.UserData).Value; CP.RemoveClaim(new Claim(ClaimTypes.UserData,AccountNo)); CP.AddClaim(new Claim(ClaimTypes.UserData, value)); } } 

cuando bash eliminar el reclamo, obtengo esta excepción:

La reclamación ‘ http://schemas.microsoft.com/ws/2008/06/identity/claims/userdata : 101’ no se pudo eliminar. No es parte de esta identidad o es un reclamo propiedad del director que contiene esta identidad. Por ejemplo, el Principal será el propietario del reclamo al crear un GenericPrincipal con roles. Los roles se expondrán a través de la identidad que se pasa en el constructor, pero que en realidad no es propiedad de la identidad. Existe una lógica similar para un RolePrincipal.

¿Alguien podría ayudarme a descubrir cómo actualizar el Reclamo?

Creé un método de extensión para añadir / actualizar / leer reclamaciones en función de una ClaimsIdentity determinada

 namespace Foobar.Common.Extensions { public static class Extensions { public static void AddUpdateClaim(this IPrincipal currentPrincipal, string key, string value) { var identity = currentPrincipal.Identity as ClaimsIdentity; if (identity == null) return; // check for existing claim and remove it var existingClaim = identity.FindFirst(key); if (existingClaim != null) identity.RemoveClaim(existingClaim); // add new claim identity.AddClaim(new Claim(key, value)); var authenticationManager = HttpContext.Current.GetOwinContext().Authentication; authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(identity), new AuthenticationProperties() { IsPersistent = true }); } public static string GetClaimValue(this IPrincipal currentPrincipal, string key) { var identity = currentPrincipal.Identity as ClaimsIdentity; if (identity == null) return null; var claim = identity.Claims.FirstOrDefault(c => c.Type == key); return claim.Value; } } } 

y luego usarlo

 using Foobar.Common.Extensions; namespace Foobar.Web.Main.Controllers { public class HomeController : Controller { public ActionResult Index() { // add/updating claims User.AddUpdateClaim("key1", "value1"); User.AddUpdateClaim("key2", "value2"); User.AddUpdateClaim("key3", "value3"); } public ActionResult Details() { // reading a claim var key2 = User.GetClaim("key2"); } } } 

Puede crear un nuevo ClaimsIdentity y luego hacer la actualización de reclamos con tal.

 set { // get context of the authentication manager var authenticationManager = HttpContext.GetOwinContext().Authentication; // create a new identity from the old one var identity = new ClaimsIdentity(User.Identity); // update claim value identity.RemoveClaim(identity.FindFirst("AccountNo")); identity.AddClaim(new Claim("AccountNo", value)); // tell the authentication manager to use this new identity authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant( new ClaimsPrincipal(identity), new AuthenticationProperties { IsPersistent = true } ); } 

Otro enfoque (asincrónico), que utiliza Identity’s UserManager y SigninManager para reflejar el cambio en la cookie de identidad (y para eliminar opcionalmente las reclamaciones de la tabla db AspNetUserClaims):

 // Get User and a claims-based identity ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); var Identity = new ClaimsIdentity(User.Identity); // Remove existing claim and replace with a new value await UserManager.RemoveClaimAsync(user.Id, Identity.FindFirst("AccountNo")); await UserManager.AddClaimAsync(user.Id, new Claim("AccountNo", value)); // Re-Signin User to reflect the change in the Identity cookie await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); // [optional] remove claims from claims table dbo.AspNetUserClaims, if not needed var userClaims = UserManager.GetClaims(user.Id); if (userClaims.Any()) { foreach (var item in userClaims) { UserManager.RemoveClaim(user.Id, item); } } 

Obtengo esa excepción también y aclaro las cosas así

 var identity = User.Identity as ClaimsIdentity; var newIdentity = new ClaimsIdentity(identity.AuthenticationType, identity.NameClaimType, identity.RoleClaimType); newIdentity.AddClaims(identity.Claims.Where(c => false == (c.Type == claim.Type && c.Value == claim.Value))); // the claim has been removed, you can add it with a new value now if desired AuthenticationManager.SignOut(identity.AuthenticationType); AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, newIdentity); 

cuando uso MVC5, y agrego el reclamo aquí.

 public async Task GenerateUserIdentityAsync(PATAUserManager manager) { // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie); // Add custom user claims here userIdentity.AddClaim(new Claim(ClaimTypes.Role, this.Role)); return userIdentity; } 

cuando compruebo el resultado del reclamo en la función SignInAsync, no puedo usar el valor del rol de todos modos. Pero…

después de que esta solicitud finalice, puedo acceder a Role en otra acción (solicitud antera).

  var userWithClaims = (ClaimsPrincipal)User; Claim CRole = userWithClaims.Claims.First(c => c.Type == ClaimTypes.Role); 

entonces, creo que tal vez sea asincrónico porque el IEnumerable se actualiza detrás del proceso.

Para eliminar los detalles de reclamo de la base de datos, podemos usar el código a continuación. Además, tenemos que iniciar sesión nuevamente para actualizar los valores de las cookies

  // create a new identity var identity = new ClaimsIdentity(User.Identity); // Remove the existing claim value of current user from database if(identity.FindFirst("NameOfUser")!=null) await UserManager.RemoveClaimAsync(applicationUser.Id, identity.FindFirst("NameOfUser")); // Update customized claim await UserManager.AddClaimAsync(applicationUser.Id, new Claim("NameOfUser", applicationUser.Name)); // the claim has been updates, We need to change the cookie value for getting the updated claim AuthenticationManager.SignOut(identity.AuthenticationType); await SignInManager.SignInAsync(Userprofile, isPersistent: false, rememberBrowser: false); return RedirectToAction("Index", "Home"); 

Múltiples cookies, múltiples reclamos

 public class ClaimsCookie { private readonly ClaimsPrincipal _user; private readonly HttpContext _httpContext; public ClaimsCookie(ClaimsPrincipal user, HttpContext httpContext = null) { _user = user; _httpContext = httpContext; } public string GetValue(CookieName cookieName, KeyName keyName) { var principal = _user as ClaimsPrincipal; var cp = principal.Identities.First(i => i.AuthenticationType == ((CookieName)cookieName).ToString()); return cp.FindFirst(((KeyName)keyName).ToString()).Value; } public async void SetValue(CookieName cookieName, KeyName[] keyName, string[] value) { if (keyName.Length != value.Length) { return; } var principal = _user as ClaimsPrincipal; var cp = principal.Identities.First(i => i.AuthenticationType == ((CookieName)cookieName).ToString()); for (int i = 0; i < keyName.Length; i++) { if (cp.FindFirst(((KeyName)keyName[i]).ToString()) != null) { cp.RemoveClaim(cp.FindFirst(((KeyName)keyName[i]).ToString())); cp.AddClaim(new Claim(((KeyName)keyName[i]).ToString(), value[i])); } } await _httpContext.SignOutAsync(CookieName.UserProfilCookie.ToString()); await _httpContext.SignInAsync(CookieName.UserProfilCookie.ToString(), new ClaimsPrincipal(cp), new AuthenticationProperties { IsPersistent = bool.Parse(cp.FindFirst(KeyName.IsPersistent.ToString()).Value), AllowRefresh = true }); } public enum CookieName { CompanyUserProfilCookie = 0, UserProfilCookie = 1, AdminPanelCookie = 2 } public enum KeyName { Id, Name, Surname, Image, IsPersistent } } 

El método de extensión funcionó muy bien para mí, con una excepción: si el usuario cierra la sesión, todavía existían conjuntos de reclamos antiguos, por lo que con una pequeña modificación como pasar el administrador de usuarios todo funciona bien y no es necesario cerrar la sesión e iniciar sesión. No puedo responder directamente ya que mi reputación ha sido desacreditada 🙁

 public static class ClaimExtensions { public static void AddUpdateClaim(this IPrincipal currentPrincipal, string key, string value, ApplicationUserManager userManager) { var identity = currentPrincipal.Identity as ClaimsIdentity; if (identity == null) return; // check for existing claim and remove it var existingClaim = identity.FindFirst(key); if (existingClaim != null) { RemoveClaim(currentPrincipal, key, userManager); } // add new claim var claim = new Claim(key, value); identity.AddClaim(claim); var authenticationManager = HttpContext.Current.GetOwinContext().Authentication; authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(identity), new AuthenticationProperties() { IsPersistent = true }); //Persist to store userManager.AddClaim(identity.GetUserId(),claim); } public static void RemoveClaim(this IPrincipal currentPrincipal, string key, ApplicationUserManager userManager) { var identity = currentPrincipal.Identity as ClaimsIdentity; if (identity == null) return ; // check for existing claim and remove it var existingClaims = identity.FindAll(key); existingClaims.ForEach(c=> identity.RemoveClaim(c)); //remove old claims from store var user = userManager.FindById(identity.GetUserId()); var claims = userManager.GetClaims(user.Id); claims.Where(x => x.Type == key).ToList().ForEach(c => userManager.RemoveClaim(user.Id, c)); } public static string GetClaimValue(this IPrincipal currentPrincipal, string key) { var identity = currentPrincipal.Identity as ClaimsIdentity; if (identity == null) return null; var claim = identity.Claims.First(c => c.Type == key); return claim.Value; } public static string GetAllClaims(this IPrincipal currentPrincipal, ApplicationUserManager userManager) { var identity = currentPrincipal.Identity as ClaimsIdentity; if (identity == null) return null; var claims = userManager.GetClaims(identity.GetUserId()); var userClaims = new StringBuilder(); claims.ForEach(c => userClaims.AppendLine($"
  • {c.Type}, {c.Value}
  • ")); return userClaims.ToString(); } }

    Aqui tienes:

      var user = User as ClaimsPrincipal; var identity = user.Identity as ClaimsIdentity; var claim = (from c in user.Claims where c.Type == ClaimTypes.UserData select c).Single(); identity.RemoveClaim(claim); 

    tomado de aquí.