MVC5 (VS2012) Identity CreateIdentityAsync – El valor no puede ser nulo

Estoy intentando configurar OAuth para un sitio MVC5 (en VS2012).

Estoy usando Fluent NHibernate. Configuré mi propia tienda de usuarios y paso un objeto de repository para acceder al objeto de sesión de NHibernate. Paso mi tienda al proveedor de administrador de usuario aspnet predeterminado. Esto finalmente funcionó para el registro local y el inicio de sesión. No estoy tratando de configurar / conectarme a Facebook.

Se obtiene una cuenta exitosa. Agrega un usuario en la tabla de usuarios, agrega un registro en la tabla de inicios de sesión y luego explota. No implementé las reclamaciones en la tienda del usuario ni puse una colección de reclamaciones en el objeto del usuario. (no estoy seguro de si esto es realmente necesario, estaba desmantelando todo lo que podría estar fallando para encontrar el origen del problema).

La línea que explota es, (en el controlador de cuenta):

var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); 

dentro de este método:

 private async Task SignInAsync(IdentityUser user, bool isPersistent) 

este es el final del seguimiento de la stack

 [ArgumentNullException: Value cannot be null. Parameter name: value] System.Security.Claims.Claim..ctor(String type, String value, String valueType, String issuer, String originalIssuer, ClaimsIdentity subject, String propertyKey, String propertyValue) +14108789 System.Security.Claims.Claim..ctor(String type, String value, String valueType) +62 Microsoft.AspNet.Identity.d__0.MoveNext() +481 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +49 Web.Controllers.d__42.MoveNext() in d:\Google Drive\Development\GoalManagement\Web\Controllers\AccountController.cs:375 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84 Web.Controllers.d__35.MoveNext() in d:\Google Drive\Development\GoalManagement\Web\Controllers\AccountController.cs:311 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84 public class IdentityUser : IUser { public IdentityUser() { Logins = new List(); } public string Id { get; set; } public string UserName { get; set; } public string PasswordHash { get; set; } public string SecurityStamp { get; set; } public IList Logins { get; set; } } public class IdentityUserLogin { public string LoginProvider { get; set; } public string ProviderKey { get; set; } } 

Puedo incluir mi código de tienda de usuarios si lo desea: no lo puse, ya que es un archivo grande y podría restarle importancia al problema.

No estoy seguro de por qué está intentando crear el objeto de reclamo y por qué está explotando. Como solo tengo VS2012, he estado parcheándolo todo junto con ejemplos en línea principalmente.


Como lo sugirió @Shoe, heredé de UserManager :

 public class NHibernateAspnetUserManager : UserManager where TUser : IdentityUser { public NHibernateAspnetUserManager(IUserStore store) : base(store) { } public override Task CreateIdentityAsync(TUser user, string authenticationType) { ClaimsIdentity identity = new ClaimsIdentity(); return Task.FromResult(identity); } } 

Ahora ya no arroja un error, pero en realidad no me autentica cuántas veces utilizo el registro / inicio de sesión de Facebook.


Para resumir. Con la información de @ Shoe, intenté anular UserManager.CreateIdentityAsync con:

 public override Task CreateIdentityAsync(TUser user, string authenticationType) { var identity = new ClaimsIdentity(); identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName)); return Task.FromResult(identity); } 

y también tratando de implementar IUserClaimStore con el retorno predeterminado (lista vacía).

El primero no pasará por un error, pero no terminará autenticado. El más tarde seguirá a través de la extraña reclamación error “System.Security.Claims.Claim..ctor”

EDITAR

Descubrió por qué se estaba produciendo el error ctor. El objeto de usuario regresaba sin la ID, por lo que el UserManager predeterminado se estaba molestando. UserManager eso y usé el UserManager predeterminado que ahora ya no arroja un error, pero que aún no registra al usuario. El objeto de identidad que devuelve se ve bien por lo que puedo decir.

Tuve el mismo error en el pasado, pero solo cuando creé el usuario con Entity Framework Migration Tool. Al crear un usuario y al iniciar sesión en el sitio web, no tuve ningún error.

Mi error fue que no estaba proporcionando un SecurityStamp con migración.

 SecurityStamp = Guid.NewGuid().ToString() 

Este conjunto de propiedades, todo funcionó.

Tuve un problema similar. La Solución era establecer la SecurityStamp-Property de la User-Entity.

Antecedentes: el cliente desea tener cuentas de administrador / superusuario con contraseñas en la base de datos y un grupo de usuarios adicionales, que podrán iniciar sesión sin contraseña, en un archivo XML …

Así que heredo de Entity Framework UserStore, anulo FindByIdAsync y FindByNameAsync, busco el archivo XML para el usuario y devuelvo una nueva User-Entity. (si no se encontró un usuario por la implementación predeterminada)

Tuve la misma excepción que Jon al crear una ClaimsIdentity.

Después de excavar, descubrí que mis nuevas entidades creadas no tenían SecurityStamp. Y el UserManager predeterminado de asp.net espera un SecurityStamp y desea establecerlo como un reclamo en ClaimsIdentity.

Después de establecer un valor para esa propiedad, utilicé una cadena que contiene un prefijo y el nombre de usuario, todo funciona bien para mí.

Hice lo mismo que @ user3347549.

Me llevó un tiempo descubrir de dónde venía realmente el error, ¡felicitaciones por encontrar a Pétice!

Estoy usando mi propia implementación de UserManager y UserStore, porque quería los tipos Guid (uniqueidentifier en MSSQL) como claves, y no como cadena (aunque solo son marcadores de posición para Guids)

Gracias a este enlace y específicamente esta respuesta, que he incluido como referencia en caso de que el enlace desaparezca, por HaoK (@Hao Kung aquí en SO):

Deberías sembrar el sello de seguridad con algo aleatorio, como un nuevo trabajo guid.

Implementé mi propia ClaimsIdentityFactory (que se ve exactamente igual de lo que encuentro en dotPeek) y simplemente modifiqué una línea en el método CreateAsync

 public class ClaimsIdentityFactory : IClaimsIdentityFactory where TUser : class, IUser where TKey : IEquatable { ///  /// Claim type used for role claims ///  public string RoleClaimType { get; set; } ///  /// Claim type used for the user name ///  public string UserNameClaimType { get; set; } ///  /// Claim type used for the user id ///  public string UserIdClaimType { get; set; } ///  /// Claim type used for the user security stamp ///  public string SecurityStampClaimType { get; set; } ///  /// Constructor ///  public ClaimsIdentityFactory() { RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"; UserIdClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"; UserNameClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"; SecurityStampClaimType = "AspNet.Identity.SecurityStamp"; } ///  /// Create a ClaimsIdentity from a user ///  ///  ///  ///  ///  ///  ///  ///  ///  public virtual async Task CreateAsync(UserManager manager, TUser user, string authenticationType) { if (manager == null) throw new ArgumentNullException("manager"); if (user == null) throw new ArgumentNullException("user"); var id = new ClaimsIdentity(authenticationType, UserNameClaimType, RoleClaimType); id.AddClaim(new Claim(UserIdClaimType, ConvertIdToString(user.Id), "http://www.w3.org/2001/XMLSchema#string")); id.AddClaim(new Claim(UserNameClaimType, user.UserName, "http://www.w3.org/2001/XMLSchema#string")); id.AddClaim(new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string")); if (manager.SupportsUserSecurityStamp) { ClaimsIdentity claimsIdentity1 = id; string securityStampClaimType = SecurityStampClaimType; ClaimsIdentity claimsIdentity2 = claimsIdentity1; string str = await manager.GetSecurityStampAsync(user.Id).ConfigureAwait(false); Claim claim = new Claim(securityStampClaimType, str ?? Guid.NewGuid().ToString()); claimsIdentity2.AddClaim(claim); } if (manager.SupportsUserRole) { IList roles = await manager.GetRolesAsync(user.Id).ConfigureAwait(false); foreach (string str in roles) id.AddClaim(new Claim(RoleClaimType, str, "http://www.w3.org/2001/XMLSchema#string")); } if (manager.SupportsUserClaim) id.AddClaims(await manager.GetClaimsAsync(user.Id).ConfigureAwait(false)); return id; } ///  /// Convert the key to a string, by default just calls .ToString() ///  ///  ///  ///  ///  protected virtual string ConvertIdToString(TKey key) { if ((object)key == null) throw new ArgumentNullException("key"); else return key.ToString(); } } 

La línea que alteré era de

 Claim claim = new Claim(securityStampClaimType, str); 

a

 Claim claim = new Claim(securityStampClaimType, str ?? Guid.NewGuid().ToString()); 

Todavía tengo que descubrir lo que esto significa, pero al menos funciona por ahora y puedo continuar probando mi aplicación. Supongo que aparece este error porque no he implementado por completo alguna parte de la stack de identidad. Para usar esta nueva fábrica, simplemente escríbala en el constructor UserManager:

 ClaimsIdentityFactory = new ClaimsIdentityFactory(); 

El UserManager predeterminado intentará obtener los reclamos y agregará / eliminará reclamos, incluso si no los ha implementado. Si no necesita reclamos, la solución que he encontrado es implementar su propio UserManager o implementar métodos de “no hacer nada” en su UserStore .

 public Task AddClaimAsync(TUser user, Claim claim) { return Task.FromResult(0); } public Task> GetClaimsAsync(TUser user) { return Task.FromResult>(new List()); } public Task RemoveClaimAsync(TUser user, Claim claim) { return Task.FromResult(0); } 

Tuve que implementar ClaimsIdentityFactory y establecer la propiedad UserManager.ClaimsIdentityFactory, es decir, en la clase AccountController.

En mi caso, fue algo totalmente diferente. Era una cuestión de orden de código de inicio de Owin

Mi código de error:

 public void ConfigureAuth(IAppBuilder app) { //... app.CreatePerOwinContext(ApplicationDbContext.Create); app.CreatePerOwinContext(AppUserManager.Create); app.CreatePerOwinContext(AppSignInManager.Create); app.CreatePerOwinContext(AppRoleManager.Create); //... } 

Resulta que AppSignInManager estaba intentando AppUserManager que siempre es null porque aún no se ha agregado a Owin.

Simplemente intercambiándolos, todo funcionó como un encanto

 public void ConfigureAuth(IAppBuilder app) { //... app.CreatePerOwinContext(ApplicationDbContext.Create); app.CreatePerOwinContext(AppSignInManager.Create); app.CreatePerOwinContext(AppUserManager.Create); app.CreatePerOwinContext(AppRoleManager.Create); //... }