In my Login method in the account controller, I have the following code
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// This doen't count login failures towards lockout only two factor authentication
// To enable password failures to trigger lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: true);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
When I enter a valid Email but invalid Password, the following methods are called:
GetLockoutEnabledAsync - For now I just always return true, will worry about admin user later
GetLockoutEndDateAsync - if the user has locked his account previously, I return 1000 days from now, otherwise a day before today.
Then a call to The UserPassword Store GetPasswordHashAsync method is called. Followed by a call to my PasswordHasher, VerifyHashedPassword
Everything up until here seems to work fine. I can see if the password passes validation or not. I use the following to return success or failure
class K3PasswordHasher : IPasswordHasher
{
private readonly K3PasswordHash _k3PasswordHash = new K3PasswordHash();
public string HashPassword(string password)
{
return _k3PasswordHash.CreateHash(password);
}
public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
if (_k3PasswordHash.ValidatePassword(providedPassword, hashedPassword))
return PasswordVerificationResult.Success;
return PasswordVerificationResult.Failed;
}
}
Now I get confused. If the password validation fails, the following methods in the UserLockoutStore are called (after getting a fresh version of the User):
IncrementAccessFailedCountAsync - This makes sense. I want to increase the failure count on the user in the database.
SetLockoutEndDateAsync- I have no idea why this method is being called. There has been no lockout yet to the best best of my knowledge, so I return an offset. For clarity, I don't care about lockout end dates. If an account is locked, it will remain so
until my reset process is used.
ResetAccessFailedCountAsync - This totally defeats what I just did by increasing the failed count. I have no idea why this call is in the stack and can not figure out what is causing the reset.
Here is the code from my UserStore. Any ideas would be appreciated.
using AutoMapper;
using K3LoginDomain;
using K3LoginRepo;
using Microsoft.AspNet.Identity;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace K3Identity
{
public class K3UserStore : IUserStore<K3IdentityUser, int>,
IUserRoleStore<K3IdentityUser, int>,
IUserPasswordStore<K3IdentityUser, int>,
IUserLockoutStore<K3IdentityUser, int>,
IUserTwoFactorStore<K3IdentityUser, int>
{
private LoginAccessor _loginAccessor = new LoginAccessor();
private bool _disposed = false;
public K3UserStore()
{
ConfigureAutoMapper();
new K3UserStore(new K3DbContext());
}
public K3UserStore(K3DbContext k3DbContext)
{
ConfigureAutoMapper();
_loginAccessor = new LoginAccessor();
}
private void ConfigureAutoMapper()
{
Mapper.CreateMap<K3IdentityUser, UserProfile>()
.ForMember(d => d.Address, o => o.Ignore())
.ForMember(d => d.Address, o => o.Ignore())
.ForMember(d => d.ObjectState, o => o.Ignore())
.ForMember(d => d.PasswordHistories, o => o.Ignore())
.ForMember(d => d.Role, o => o.Ignore())
.ForMember(d => d.SecQues1, o => o.Ignore())
.ForMember(d => d.SecQues2, o => o.Ignore())
.ForMember(d => d.SecQues3, o => o.Ignore())
.ForMember(d => d.SecQues4, o => o.Ignore())
.ForMember(d => d.SecQues5, o => o.Ignore())
.ForMember(d => d.Theme, o => o.Ignore())
.ForMember(d => d.UserPhones, o => o.Ignore());
Mapper.CreateMap<UserProfile, K3IdentityUser>()
.ForMember(d => d.PasswordHash, o => o.Ignore());
Mapper.AssertConfigurationIsValid();
}
#region UserStore
public Task CreateAsync(K3IdentityUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
UserProfile userProfile = Mapper.Map<K3IdentityUser, UserProfile>(user);
if (!_loginAccessor.Repo.InsertUserProfile(userProfile, false))
{
throw new ApplicationException("Unable to insert user.");
}
_loginAccessor.Save();
return Task.FromResult<object>(null);
}
public Task DeleteAsync(K3IdentityUser user)
{
if (user.Id > 0)
{
if (!_loginAccessor.Repo.DeleteUserProfile(user.Id))
{
throw new Exception("Can not delete user.");
}
}
return Task.FromResult<Object>(null);
}
public Task<K3IdentityUser> FindByIdAsync(int userId)
{
if (userId <= 0)
{
throw new ArgumentException("Null or empty argument: id");
}
UserProfile user = _loginAccessor.Repo.GetUserProfile(userId);
if (user == null)
{
return Task.FromResult<K3IdentityUser>(null);
}
K3IdentityUser k3User = Mapper.Map<UserProfile, K3IdentityUser>(user);
return Task.FromResult<K3IdentityUser>(k3User);
}
public Task<K3IdentityUser> FindByNameAsync(string userName)
{
if (string.IsNullOrEmpty(userName))
{
throw new ArgumentException("Null or empty argument: userName");
}
UserProfile user = _loginAccessor.Repo.GetUserProfileByUserName(userName);
if (user == null)
{
return Task.FromResult<K3IdentityUser>(null);
}
K3IdentityUser k3User = Mapper.Map<UserProfile, K3IdentityUser>(user);
return Task.FromResult<K3IdentityUser>(k3User);
}
public Task UpdateAsync(K3IdentityUser user)
{
UserProfile userProfile = _loginAccessor.Repo.GetUserProfile(user.Id);
if (user == null)
{
throw new ArgumentNullException("user");
}
if (userProfile.Password != user.Password)
{
throw new Exception("Use Change Password Function");
}
UserProfile newUserProfile = Mapper.Map<K3IdentityUser,UserProfile>(user);
_loginAccessor.Repo.UpdateUserProfile(newUserProfile, false, false);
return Task.FromResult<object>(null);
}
#endregion
#region UserLockoutStore
public Task<int> GetAccessFailedCountAsync(K3IdentityUser user)
{
return Task.FromResult(user.PasswordFailures);
}
public Task<bool> GetLockoutEnabledAsync(K3IdentityUser user)
{
return Task.FromResult(true);
}
public Task<DateTimeOffset> GetLockoutEndDateAsync(K3IdentityUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
UserProfile userProfile = _loginAccessor.Repo.GetUserProfile(user.Id);
PasswordRequirement passwordRequirement = _loginAccessor.Repo.GetPasswordRequirement();
if (userProfile.PasswordFailures >= passwordRequirement.PasswordFailsAllowed)
return Task.FromResult(DateTimeOffset.UtcNow.AddDays(1000));
return Task.FromResult(DateTimeOffset.UtcNow.Subtract(new TimeSpan(1,0,0,0)));
}
public Task<int> IncrementAccessFailedCountAsync(K3IdentityUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
UserProfile userProfile = _loginAccessor.Repo.GetUserProfile(user.Id);
userProfile.PasswordFailures++;
if (_loginAccessor.Repo.UpdateUserProfile(userProfile, false, false));
_loginAccessor.Save();
return Task.FromResult(userProfile.PasswordFailures);
}
public Task ResetAccessFailedCountAsync(K3IdentityUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
UserProfile userProfile = _loginAccessor.Repo.GetUserProfile(user.Id);
userProfile.PasswordFailures = 0;
if (_loginAccessor.Repo.UpdateUserProfile(userProfile, false, false))
_loginAccessor.Save();
return Task.FromResult(0);
}
public Task SetLockoutEnabledAsync(K3IdentityUser user, bool enabled)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
UserProfile userProfile = _loginAccessor.Repo.GetUserProfile(user.Id);
PasswordRequirement passwordRequirement = _loginAccessor.Repo.GetPasswordRequirement();
if (userProfile.PasswordFailures >= passwordRequirement.PasswordFailsAllowed)
return Task.FromResult(DateTimeOffset.UtcNow.AddDays(1000));
return Task.FromResult(0);
}
public Task SetLockoutEndDateAsync(K3IdentityUser user, DateTimeOffset lockoutEnd)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
UserProfile userProfile = _loginAccessor.Repo.GetUserProfile(user.Id);
PasswordRequirement passwordRequirement = _loginAccessor.Repo.GetPasswordRequirement();
if (userProfile.PasswordFailures >= passwordRequirement.PasswordFailsAllowed)
return Task.FromResult(DateTimeOffset.UtcNow.AddDays(1000));
return Task.FromResult(DateTimeOffset.UtcNow.Subtract(new TimeSpan(1, 0, 0, 0)));
}
#endregion
#region UserPasswordStore
public Task<string> GetPasswordHashAsync(K3IdentityUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
string passwordHash = _loginAccessor.Repo.GetUserProfile(user.Id).Password;
return Task.FromResult<string>(passwordHash);
}
public Task SetPasswordHashAsync(K3IdentityUser user, string passwordHash)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (passwordHash == null)
{
throw new ArgumentNullException("user");
}
if (user.Id > 0)
{
UserProfile userProfile = _loginAccessor.Repo.GetUserProfile(user.Id);
userProfile.Password = passwordHash;
if (!_loginAccessor.Repo.UpdateUserProfile(userProfile, false, false))
throw new Exception("Can not update password.");
}
user.Password = passwordHash;
user.PasswordHash = passwordHash;
return Task.FromResult<Object>(null);
}
public Task<bool> HasPasswordAsync(K3IdentityUser user)
{
var hasPassword = !string.IsNullOrEmpty(_loginAccessor.Repo.GetUserProfile(user.Id).Password);
return Task.FromResult<bool>(Boolean.Parse(hasPassword.ToString()));
}
#endregion
#region UserRoleStore
public Task AddToRoleAsync(K3IdentityUser user, string roleName)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (string.IsNullOrEmpty(roleName))
{
throw new ArgumentException("Argument cannot be null or empty: roleName.");
}
Role role = _loginAccessor.Repo.GetRoleByName(roleName);
UserProfile userProfile = _loginAccessor.Repo.GetUserProfile(user.Id);
userProfile.RoleId = role.Id;
_loginAccessor.Repo.UpdateUserProfile(userProfile, false, false);
_loginAccessor.Save();
return Task.FromResult<object>(null);
}
public Task RemoveFromRoleAsync(K3IdentityUser user, string roleName)
{
throw new NotImplementedException();
}
public Task<IList<string>> GetRolesAsync(K3IdentityUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
UserProfile userProfile = _loginAccessor.Repo.GetUserProfile(user.Id);
Role role = _loginAccessor.Repo.GetRole(userProfile.RoleId);
List<string> roleList = new List<string>();
roleList.Add(role.RoleDes);
return Task.FromResult<IList<string>>(roleList);
}
public Task<bool> IsInRoleAsync(K3IdentityUser user, string roleName)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (string.IsNullOrEmpty(roleName))
{
throw new ArgumentNullException("roleName");
}
UserProfile userProfile = _loginAccessor.Repo.GetUserProfile(user.Id);
Role role = _loginAccessor.Repo.GetRole(userProfile.RoleId);
if (role != null && role.RoleDes == roleName)
{
return Task.FromResult<bool>(true);
}
return Task.FromResult<bool>(false);
}
#endregion
#region UserTwoFactor
public Task SetTwoFactorEnabledAsync(K3IdentityUser user, bool enabled)
{
throw new NotImplementedException();
}
public Task<bool> GetTwoFactorEnabledAsync(K3IdentityUser user)
{
return Task.FromResult(false);
}
#endregion
#region Dispose
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
if (_loginAccessor != null)
{
_loginAccessor.Dispose();
_loginAccessor= null;
}
}
_disposed = true;
}
#endregion
}
}
Thanks Michelle. I removed the automapper functionality and mapped the properties manually. No difference in the results. I have been stepping through the code in Debug and have since added my own override in my user manager to the SignInAsynch method.
In here I can determine not only password validation, but increase the failure count as desired. However, the PasswordSignInAsynch method in the SignInMnager must have the logic that is causing my issue. That, or perhaps the authentication manager. I say
this because after my UserManager Sign in process runs, calls to the lockout store to increase failure count still occurs and after that is called, it is still calling the reset method to return the count to 0. Is the source code for the SignInManager public
domain? And do you know if it is possible to write a custom one which inherits or overrides the one provided? The solution I am now leaning toward is removing anything to do with the SignInManager and calling the UserStore directly as I need. This is certainly
not what I want to do as I would prefer to stay within the Microsoft architecture so I don't run into problems with future releases
Ever get any answer on this? I am having the same issue. Can definitely code around it but feel I shouldn't have to (same point, want to stay within the architecture). My short term solution was to not do anything on the Reset call and I wrote code to detect
when to call reset myself.
Shortly after posting this found the problem - I wasn't setting the properties on the user manager:
UserManager.UserLockoutEnabledByDefault (default to false) UserManager.DefaultAccountLockoutTimeSpan (defaults to 0:00) UserManager.MaxFailedAccessAttemptsBeforeLockout (defaults to 0)
When these are not set they default to a values that basically disable the
lockout causing reset to be called every time. What threw me off was that the lockout interface was still called even though
UserLockoutEnabledByDefault was false.
None
0 Points
2 Posts
SignInManager.PasswordSignInAsynch calling ResetAccessFailedCountAsync
Aug 10, 2014 05:10 PM|Kirby2001|LINK
In my Login method in the account controller, I have the following code
When I enter a valid Email but invalid Password, the following methods are called:
GetLockoutEnabledAsync - For now I just always return true, will worry about admin user later
GetLockoutEndDateAsync - if the user has locked his account previously, I return 1000 days from now, otherwise a day before today.
Then a call to The UserPassword Store GetPasswordHashAsync method is called. Followed by a call to my PasswordHasher, VerifyHashedPassword
Everything up until here seems to work fine. I can see if the password passes validation or not. I use the following to return success or failure
Now I get confused. If the password validation fails, the following methods in the UserLockoutStore are called (after getting a fresh version of the User):
IncrementAccessFailedCountAsync - This makes sense. I want to increase the failure count on the user in the database.
SetLockoutEndDateAsync- I have no idea why this method is being called. There has been no lockout yet to the best best of my knowledge, so I return an offset. For clarity, I don't care about lockout end dates. If an account is locked, it will remain so until my reset process is used.
ResetAccessFailedCountAsync - This totally defeats what I just did by increasing the failed count. I have no idea why this call is in the stack and can not figure out what is causing the reset.
Here is the code from my UserStore. Any ideas would be appreciated.
All-Star
18232 Points
2199 Posts
Re: SignInManager.PasswordSignInAsynch calling ResetAccessFailedCountAsync
Aug 11, 2014 04:39 AM|Michelle Ge - MSFT|LINK
Hi,
According to your code, I suggest you making sure ConfigureAutoMapper function can work ok.
The same time I suggest you making some break points to debug steps by steps, to check that it can work by your logic.
Hope it's useful for you.
Best Regards,
Michelle Ge
None
0 Points
2 Posts
Re: SignInManager.PasswordSignInAsynch calling ResetAccessFailedCountAsync
Aug 11, 2014 12:06 PM|Kirby2001|LINK
Thanks Michelle. I removed the automapper functionality and mapped the properties manually. No difference in the results. I have been stepping through the code in Debug and have since added my own override in my user manager to the SignInAsynch method. In here I can determine not only password validation, but increase the failure count as desired. However, the PasswordSignInAsynch method in the SignInMnager must have the logic that is causing my issue. That, or perhaps the authentication manager. I say this because after my UserManager Sign in process runs, calls to the lockout store to increase failure count still occurs and after that is called, it is still calling the reset method to return the count to 0. Is the source code for the SignInManager public domain? And do you know if it is possible to write a custom one which inherits or overrides the one provided? The solution I am now leaning toward is removing anything to do with the SignInManager and calling the UserStore directly as I need. This is certainly not what I want to do as I would prefer to stay within the Microsoft architecture so I don't run into problems with future releases
None
0 Points
3 Posts
Re: SignInManager.PasswordSignInAsynch calling ResetAccessFailedCountAsync
Nov 25, 2014 04:26 PM|porrey|LINK
Ever get any answer on this? I am having the same issue. Can definitely code around it but feel I shouldn't have to (same point, want to stay within the architecture). My short term solution was to not do anything on the Reset call and I wrote code to detect when to call reset myself.
None
0 Points
3 Posts
Re: SignInManager.PasswordSignInAsynch calling ResetAccessFailedCountAsync
Nov 25, 2014 04:38 PM|porrey|LINK
Shortly after posting this found the problem - I wasn't setting the properties on the user manager:
UserManager.UserLockoutEnabledByDefault (default to false)
UserManager.DefaultAccountLockoutTimeSpan (defaults to 0:00)
UserManager.MaxFailedAccessAttemptsBeforeLockout (defaults to 0)
When these are not set they default to a values that basically disable the lockout causing reset to be called every time. What threw me off was that the lockout interface was still called even though UserLockoutEnabledByDefault was false.
None
0 Points
1 Post
Re: SignInManager.PasswordSignInAsynch calling ResetAccessFailedCountAsync
Jul 16, 2015 02:31 AM|Shankar Gurav|LINK
Hi Porrey,
If I set those values to
UserManager.UserLockoutEnabledByDefault = true
UserManager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
UserManager.MaxFailedAccessAttemptsBeforeLockout = 3;
my SetLockoutEndDateAsync() function is not being called at all. Do you know the ideal settings?
Thanks,
Shankar