MVC5 (VS2012) identité CreateIdentityAsync - la valeur ne peut pas être nulle

j'essaie de configurer OAuth pour un site MVC5 (en VS2012).

j'utilise du NHibernate Fluent. J'ai configuré mon propre Userstore et passe dans un objet de dépôt pour accéder à L'objet de session NHibernate. Je passe mon magasin dans le fournisseur aspnet usermanager par défaut. Cela a finalement fonctionné pour l'enregistrement local et l'ouverture de session. Je ne suis pas en train de configurer la connexion / l'enregistrement avec Facebook.

Il obtient un succès. Ajoute un utilisateur dans la table user, ajoute une enregistrement dans les connexions du tableau, puis explose. Je n'ai pas implémenté les réclamations dans le magasin utilisateur, ni mis une collection de réclamations dans l'objet utilisateur. (Je ne suis pas sûr que cela soit vraiment nécessaire, j'étais en train d'enlever tout ce qui pouvait aller mal pour trouver la source du problème).

La ligne qui explose est, (dans le compte contrôleur):

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

dans cette méthode:

private async Task SignInAsync(IdentityUser user, bool isPersistent)

c'est la fin de la trace de la pile

[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 DriveDevelopmentGoalManagementWebControllersAccountController.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 DriveDevelopmentGoalManagementWebControllersAccountController.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; }
}

je peux incluez mon code userstore si vous le souhaitez: Je ne l'ai pas mis dans Car c'est un gros fichier et pourrait nuire à la question.

Je ne suis pas sûr de savoir pourquoi il essaie de créer l'objet claim et pourquoi il explose. Comme je n'ai que VS2012, j'ai corrigé tout cela à partir d'exemples en ligne principalement.


comme suggéré par @Shoe j'ai hérité 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);
    }
}

il ne lance plus d'erreur mais ne m'authentifie plus comment jamais plusieurs fois j'utilise le Facebook register / login.


Pour résumer. Avec l'info de @Shoe j'ai essayé les deux de passer outre UserManager.CreateIdentityAsync avec:

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

et aussi essayer d'implémenter IUserClaimStore avec le retour par défaut (liste vide).

le premier ne passera pas par une erreur mais ne finira pas authentifié. Le dernier sera toujours par le système de réclamation Impair ".Sécurité.Réclamation.Réclamation..ctor" erreur

EDIT

J'ai trouvé pourquoi l'erreur de ctor se produisait. L'objet user revenait sans ID, donc par défaut UserManager a été de s'énerver. Fixe et utilisé par défaut UserManager qui ne lance plus d'erreur, mais ne se connecte toujours pas. L'objet d'identité qu'il renvoie semble bon d'après ce que je peux dire.

30
demandé sur Korayem 2014-02-20 23:47:20

6 réponses

j'ai eu la même erreur dans le passé mais seulement quand j'ai créé l'utilisateur avec L'outil de Migration Entity Framework. Lors de la création d'un utilisateur et de la signature avec le site web, Je n'avais pas d'erreur.

mon erreur était que je ne fournissais pas un SecurityStamp avec migration.

SecurityStamp = Guid.NewGuid().ToString()

cette propriété ensemble, tout a fonctionné.

75
répondu Patrick Desjardins 2014-04-29 01:51:31

j'ai eu un problème similaire. La Solution était de définir la propriété SecurityStamp-de L'entité-utilisateur.

Background: le client veut avoir des comptes Admin-/Superuser-avec des mots de passe dans la base de données et un groupe d'utilisateurs supplémentaires - qui seront en mesure de se connecter sans mot de passe - dans un fichier XML...

donc j'hérite de L'Userstore Entity Framework, j'annule FindByIdAsync et FindByNameAsync, je recherche le fichier XML pour l'utilisateur et je renvoie une nouvelle User-Entity. (si pas de l'utilisateur a été trouvé par l'implémentation par défaut)

j'ai eu la même Exception que Jon en créant une Identitédemande.

après quelques recherches, j'ai découvert que mes nouvelles entités utilisatrices n'avaient pas de SecurityStamp. Et le asp.net UserManager par défaut attend une SecurityStamp et veut la définir comme une revendication dans L'Entitédemande.

après avoir défini une valeur à cette propriété-j'ai utilisé une chaîne qui contient un préfixe et le nom d'utilisateur - tout fonctionne très bien pour je.

13
répondu Andreas Pircher 2014-04-09 20:07:09

j'ai fait la même chose que @user3347549.

il m'a fallu du temps pour comprendre d'où venait l'erreur, bravo à dotPeek pour ça!

j'utilise ma propre implémentation de UserManager et UserStore, parce que je voulais des types de Guid (uniqueidentifier dans MSSQL) comme clés, et non des chaînes (bien qu'ils soient juste des placeholders pour les GUID)

merci à lien et en particulier cette réponse, que j'ai inclus à titre de référence dans le cas le lien disparaît, de HaoK (@Hao Kung ici SO):

vous devriez ensemencer le tampon de sécurité avec quelque chose d'aléatoire, comme un nouveau travail de guid.

j'ai implémenté mon propre ClaimsIdentityFactory (qui ressemble exactement au même de ce que j'ai trouvé dans dotPeek) et j'ai juste modifié une ligne dans la méthode 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 ligne I a été modifié à partir de

Claim claim = new Claim(securityStampClaimType, str);

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

je n'ai pas encore de comprendre ce que cela signifie, mais au moins cela fonctionne pour le moment et je peux continuer à tester mon application. Je suppose que cette erreur apparaît parce que je n'ai pas entièrement implémenté une partie de la pile D'identité. Pour utiliser cette nouvelle usine, tapez simplement ceci dans le UserManager constructeur:

ClaimsIdentityFactory = new ClaimsIdentityFactory<TUser, Guid>();
2
répondu Chris Klingsater 2014-03-25 09:58:19

par défaut UserManager tentera d'obtenir les revendications et ajouter/supprimer des revendications même si vous n'avez pas mis en œuvre. Si vous n'avez pas besoin de réclamations, la solution que j'ai trouvé est de mettre en œuvre votre propre UserManager ou implémenter des méthodes "ne rien faire" dans votre 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);
}
1
répondu Shoe 2014-02-20 20:08:33

J'ai dû implémenter ClaimsIdentityFactory et mettre en place le UserManager.ClaimsIdentityFactory propriété, c.-à-d. Dans la classe AccountController.

0
répondu 2014-02-24 16:31:27

Dans mon cas, c'était quelque chose de totalement différent. C'était une question D'ordre de code de démarrage D'Owin

mon code 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);

   //...

}

Il s'avère, AppSignInManager essayait d'init AppUserManager toujours null parce qu'il n'a pas encore été ajouté à Owin.

en les échangeant simplement ensemble, tout a fonctionné comme un charme

public void ConfigureAuth(IAppBuilder app)
{

   //...

   app.CreatePerOwinContext(ApplicationDbContext.Create);
   app.CreatePerOwinContext<AppSignInManager>(AppSignInManager.Create);
   app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);
   app.CreatePerOwinContext<AppRoleManager>(AppRoleManager.Create);

   //...

}
0
répondu Korayem 2015-11-29 12:39:41