MVC5 (VS2012) Identity CreateIdentityAsync – значение не может быть нулевым

Я пытаюсь настроить OAuth для сайта MVC5 (в VS2012).

Я использую Fluent NHibernate. Я установил свой собственный Userstore и передал объект репозитория для доступа к объекту сеанса NHibernate. Я передаю свой магазин в поставщик aspnet usermanager по умолчанию. Это в конечном итоге работало для локальной регистрации и входа в систему. Я не пытаюсь настроить подключение / регистрацию в Facebook.

Он получает успешную учетную запись. Добавляет пользователя в пользовательскую таблицу, добавляет запись в таблицу логинов и затем взрывается. Я не реализую заявки в хранилище пользователей или помещаю коллекцию претензий в объект пользователя. (не уверен, что это действительно требуется, я все время отбрасывал все, что могло бы пойти не так, чтобы найти источник проблемы).

Линия, которая взрывается, – это (в controllerе учетной записи):

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

в рамках этого метода:

 private async Task SignInAsync(IdentityUser user, bool isPersistent) 

это конец трассировки стека

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

Я могу включить свой код пользовательского магазина, если захочу: я не помещал его, так как это большой файл и мог бы отвлечься от проблемы.

Я не уверен, почему он даже пытается создать объект претензии и почему он взрывается. Поскольку у меня только VS2012, я все время собирал все это из примеров в Интернете.


Как было предложено @Shoe, я унаследовал от 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); } } 

Теперь он больше не выдает ошибку, но фактически не аутентифицирует меня, как много раз я пользуюсь регистрацией / регистрацией в Facebook.


Обобщить. С информацией @ Shoe я попробовал как переопределить UserManager.CreateIdentityAsync с помощью:

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

а также пытается реализовать IUserClaimStore с возвращаемым значением по умолчанию (пустой список).

Первый не будет проходить через ошибку, но не завершится аутентификацией. Позднее все еще будет возникать нечетное требование «Ошибка System.Security.Claims.Claim..ctor»

РЕДАКТИРОВАТЬ

Выяснил, почему произошла ошибка ctor. Пользовательский объект возвращался без идентификатора, поэтому пользовательский пользовательский UserManager расстраивался. Исправлено это и использовалось UserManager по умолчанию, который теперь больше не выдает ошибку, но до сих пор не регистрирует пользователя. Объект идентификации, который он возвращает, выглядит хорошо из того, что я могу сказать.

У меня была такая же ошибка в прошлом, но только тогда, когда я создал пользователя с инструментом миграции объектов Entity Framework. При создании пользователя и подписании с сайтом я не ошибся.

Моя ошибка заключалась в том, что я не обеспечивал миграцию SecurityStamp .

 SecurityStamp = Guid.NewGuid().ToString() 

Это свойство, все сработало.

У меня была аналогичная проблема. Решение состояло в том, чтобы установить SecurityStamp-Property для объекта-пользователя.

Предпосылки: клиент хочет иметь учетные записи администратора / суперпользователя с паролями в базе данных и кучу дополнительных пользователей, которые могут войти в систему без пароля, в файле XML …

Поэтому я наследую Entity Framework UserStore, переопределяет FindByIdAsync и FindByNameAsync, просматривает XML-файл для пользователя и возвращает новый User-Entity. (если ни один пользователь не был найден по умолчанию)

У меня было такое же Исключение, как у Джона при создании ClaimsIdentity.

После некоторого рытья я обнаружил, что у моих недавно созданных пользовательских объектов не было SecurityStamp. И UserManager по умолчанию asp.net ожидает SecurityStamp и хочет установить его как претензию в ClaimsIdentity.

После установки значения для этого свойства – я использовал строку, содержащую префикс и имя пользователя – все работает отлично для меня.

Я сделал то же самое, что и @ user3347549.

Мне потребовалось некоторое время, чтобы выяснить, откуда на самом деле исходит ошибка: для этого вам пригодится dotPeek!

Я использую собственную реализацию UserManager и UserStore, потому что мне нужны типы Guid (uniqueidentifier в MSSQL) в качестве ключей, а не строка (хотя они просто заполнители для гидов)

Благодаря этой ссылке и, в частности, этому ответу, который я включил для ссылки в случае, если ссылка уходит, HaoK (@Hao Kung here on SO):

Вы должны запечатать штамп безопасности чем-то случайным, как новое руководство.

Я реализовал свой собственный ClaimsIdentityFactory (который выглядит точно так же от того, что я собираю в dotPeek), и просто изменил одну строку в методе 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(); } } 

Линия, которую я изменил,

 Claim claim = new Claim(securityStampClaimType, str); 

в

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

Мне еще предстоит выяснить, что это значит, но по крайней мере сейчас это работает, и я могу продолжить тестирование своего приложения. Я предполагаю, что эта ошибка появляется, потому что я не полностью реализовал часть стека Identity. Чтобы использовать этот новый завод, просто введите его в конструкторе UserManager:

 ClaimsIdentityFactory = new ClaimsIdentityFactory(); 

Пользователь UserManager умолчанию попытается получить претензии и добавить / удалить претензии, даже если вы их не реализовали. Если вам не нужны претензии, решение, которое я нашел, – это реализовать собственный UserManager или реализовать методы «ничего не делать» в вашем 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); } 

Мне пришлось реализовать ClaimsIdentityFactory и установить свойство UserManager.ClaimsIdentityFactory, то есть в classе AccountController.

В моем случае это было совсем другое. Это был вопрос о заказе кода запуска Owin

Мой багги код:

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

Оказывается, AppSignInManager пытался AppUserManager который всегда является null потому что он еще не добавлен в Owin.

Просто обменявшись ими, все работало как шарм

 public void ConfigureAuth(IAppBuilder app) { //... app.CreatePerOwinContext(ApplicationDbContext.Create); app.CreatePerOwinContext(AppSignInManager.Create); app.CreatePerOwinContext(AppUserManager.Create); app.CreatePerOwinContext(AppRoleManager.Create); //... } 
Давайте будем гением компьютера.