asp.net - tables - identity framework c#
MVC5(VS2012) Identity CreateIdentityAsync: el valor no puede ser nulo (6)
Estoy tratando de configurar OAuth para un sitio MVC5 (en VS2012).
Estoy usando Fluido NHibernate. He configurado mi propio Userstore y paso un objeto de repositorio para acceder al objeto de sesión NHibernate. Paso mi tienda al proveedor de administrador de usuario aspnet predeterminado. Esto eventualmente funcionó para el registro local y el inicio de sesión. No estoy tratando de configurar la conexión / registro con Facebook.
Se obtiene una cuenta exitosa. Agrega un usuario en la tabla de usuarios, agrega un registro en la tabla de inicio 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 eliminando todo lo que podría estar yendo mal para encontrar la fuente 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 de la traza de la pila.
[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.<CreateAsync>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.<SignInAsync>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.<ExternalLoginConfirmation>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<IdentityUserLogin>();
}
public string Id { get; set; }
public string UserName { get; set; }
public string PasswordHash { get; set; }
public string SecurityStamp { get; set; }
public IList<IdentityUserLogin> Logins { get; set; }
}
public class IdentityUserLogin
{
public string LoginProvider { get; set; }
public string ProviderKey { get; set; }
}
Si lo desea, puedo incluir el código de mi usuario para guardar: no lo puse porque 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 parcheando todo de ejemplos en línea principalmente.
Según lo sugerido por @Shoe heredé de UserManager
:
public class NHibernateAspnetUserManager<TUser> : UserManager<TUser> where TUser : IdentityUser
{
public NHibernateAspnetUserManager(IUserStore<TUser> store) : base(store)
{
}
public override Task<ClaimsIdentity> 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<ClaimsIdentity> 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 lo hará a través de 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 el 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.
El UserManager
predeterminado intentará obtener los reclamos y agregará / eliminará reclamos, incluso si no los ha implementado. Si no necesita reclamaciones, 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<int>(0);
}
public Task<IList<Claim>> GetClaimsAsync(TUser user)
{
return Task.FromResult<IList<Claim>>(new List<Claim>());
}
public Task RemoveClaimAsync(TUser user, Claim claim)
{
return Task.FromResult<int>(0);
}
En el pasado tuve el mismo error, pero solo cuando creé un usuario con la herramienta de migración Entity Framework. Al crear un usuario y firmar con el sitio web, no había error.
Mi error fue que no estaba proporcionando una SecurityStamp con la migración.
SecurityStamp = Guid.NewGuid().ToString()
Este conjunto de propiedades, todo funcionó.
En mi caso fue algo totalmente diferente. Era una cuestión de ordenar el código de inicio de Owin.
Mi código de buggy:
public void ConfigureAuth(IAppBuilder app)
{
//...
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);
app.CreatePerOwinContext<AppSignInManager>(AppSignInManager.Create);
app.CreatePerOwinContext<AppRoleManager>(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ó a la perfección.
public void ConfigureAuth(IAppBuilder app)
{
//...
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<AppSignInManager>(AppSignInManager.Create);
app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);
app.CreatePerOwinContext<AppRoleManager>(AppRoleManager.Create);
//...
}
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 this enlace y específicamente a esta respuesta, que he incluido como referencia en caso de que el enlace desaparezca, por HaoK (@Hao Kung aquí en SO):
Debe colocar el sello de seguridad con algo aleatorio, como un nuevo trabajo de guía.
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<TUser, TKey> : IClaimsIdentityFactory<TUser, TKey>
where TUser : class, IUser<TKey>
where TKey : IEquatable<TKey>
{
/// <summary>
/// Claim type used for role claims
/// </summary>
public string RoleClaimType { get; set; }
/// <summary>
/// Claim type used for the user name
/// </summary>
public string UserNameClaimType { get; set; }
/// <summary>
/// Claim type used for the user id
/// </summary>
public string UserIdClaimType { get; set; }
/// <summary>
/// Claim type used for the user security stamp
/// </summary>
public string SecurityStampClaimType { get; set; }
/// <summary>
/// Constructor
/// </summary>
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";
}
/// <summary>
/// Create a ClaimsIdentity from a user
/// </summary>
/// <param name="manager">
/// </param>
/// <param name="user">
/// </param>
/// <param name="authenticationType">
/// </param>
/// <returns>
/// </returns>
public virtual async Task<ClaimsIdentity> CreateAsync(UserManager<TUser, TKey> 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<string> 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;
}
/// <summary>
/// Convert the key to a string, by default just calls .ToString()
/// </summary>
/// <param name="key">
/// </param>
/// <returns>
/// </returns>
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 pila de identidad. Para usar esta nueva fábrica, simplemente escríbala en el constructor UserManager:
ClaimsIdentityFactory = new ClaimsIdentityFactory<TUser, Guid>();
Tuve que implementar ClaimsIdentityFactory y establecer la propiedad UserManager.ClaimsIdentityFactory, es decir, en la clase AccountController.
Tuve un problema similar. La solución fue establecer la propiedad SecurityStamp de la entidad de usuario.
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 heredé del Entity Framework UserStore, anulé FindByIdAsync y FindByNameAsync, busqué el archivo XML para el usuario y devolví una nueva entidad de usuario. (si la implementación predeterminada no ha encontrado ningún usuario)
Tuve la misma excepción que Jon al crear una ClaimsIdentity.
Después de algunas excavaciones, descubrí que mis entidades de usuario recién creadas no tenían una marca de seguridad. Y el UserManager predeterminado de asp.net espera un SecurityStamp y desea establecerlo como un Reclamo en la 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í.