Search code examples
asp.net-mvc-5asp.net-identity-2

usermanager.findasync returns null for code generated users


I'm having a problem with users that I create in code. They work for the first few login attempts but then after a while stop logging in and return null from the findasync method. if I do a findbyname it returns the user however this doesn't check the password so I can't use it. these users also don't have a password hash but the users that I create using the register methods do.

if (!db.Users.Any())
        {
            var userStore = new UserStore<ApplicationUser>(db);
            var userManager = new ApplicationUserManager(userStore);
            var user = new ApplicationUser
            {
                UserName = "username",
                FirstName = "first",
                LastName = "last",
                EmailAddress = "example@example.com",
                DateCreated = DateTime.Now,
                DateModified = DateTime.Now,
                ContactNumber = "0208555555",
                LastActivityDate = DateTime.Now,
                LastPasswordChangedDate = DateTime.Now
            };
            user.LockoutEnabled = false;
            userManager.Create(user, "abc123");
            userManager.AddToRole(user.Id, "Admin");
        }

public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (ModelState.IsValid)
        {
            var user = await UserManager.FindAsync(model.UserName, model.Password);
            var user2 = await UserManager.FindByNameAsync(model.UserName);
            PasswordVerificationResult result = PasswordVerificationResult.Failed;
            result = UserManager.PasswordHasher.VerifyHashedPassword(user2.PasswordHash, model.Password);
            if (user != null)
            {
                await SignInAsync(user, model.RememberMe);
                AuditLog.LogUserAction(UserManager.FindByName(model.UserName).Id, "Login", "User successfully logged in.");
                user.LastActivityDate = DateTime.Now;
                await db.SaveChangesAsync();
                return RedirectToLocal(returnUrl);
            }
            else
            {
                ModelState.AddModelError("", "Invalid username or password.");
            }
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

 public class ApplicationUser : IdentityUser
{
    [Required]
    [Display(Name = "First name")]
    [MaxLength(20)]
    public string FirstName { get; set; }

    [Required]
    [Display(Name = "Last name")]
    [MaxLength(50)]
    public string LastName { get; set; }

    [Required]
    [Display(Name = "Email")]
    [EmailAddress(ErrorMessage = "Invalid email address")]
    public string EmailAddress { get; set; }

    [Required]
    [Display(Name = "Contact number")]
    public string ContactNumber { get; set; }

    [Required]
    [Display(Name = "Service Provider")]
    public int SK_ServiceProviderID { get; set; }
    public virtual ServiceProvider ServiceProvider { get; set; }

    [Required]
    [DisplayName("Date Created")]
    public DateTime DateCreated { get; set; }

    [Required]
    [DisplayName("Date Modified")]
    public DateTime DateModified { get; set; }

    [Required]
    public DateTime LastPasswordChangedDate { get; set; }

    [Required]
    public DateTime LastActivityDate { get; set; }

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> 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
        return userIdentity;
    }
}

Solution

  • turns out that it was failing because I was not setting a security stamp field when creating the users by code and this can not be null when using the following:

    var resetToken = await UserManager.GeneratePasswordResetTokenAsync(model.UserId);
    IdentityResult passwordChangeResult = await UserManager.ResetPasswordAsync(model.UserId, resetToken, model.Password);
    

    This answer to another question helped me figure it out.