PasswordHasher Always Fails Validation - c#

I'm implementing a policy where passwords cannot be reused and have the structure in place, however I'm trying to work out how to check this against the PasswordHasher, I always get a failed match.
Please help on this..
try
{
UserStore<ApplicationUser> store = new UserStore<ApplicationUser>(context);
UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(store);
ApplicationUser cUser = await store.FindByNameAsync(model.UserName);
if (cUser == null)
{
ModelState.AddModelError("", "User ID is not correct, please check and try again.");
return BadRequest(ModelState);
}
else
{
/// Need to check the new password is already entered recently
DataController dataController = new DataController();
string[] pwds = dataController.CheckIfNewPasswordSameAsLastFivePassword(model.UserName);
if (pwds != null)
{
foreach (string pwd in pwds)
{
PasswordVerificationResult result1 = UserManager.PasswordHasher.VerifyHashedPassword(pwd, model.UserIdentifier);
if (result1 == PasswordVerificationResult.Success)
{
ModelState.AddModelError("", "Please choose a password that you have not used before");
return BadRequest(ModelState);
}
}
}
//Send new password in model.UserIdentifier
await store.SetPasswordHashAsync(cUser, UserManager.PasswordHasher.HashPassword(model.UserIdentifier));
await store.UpdateAsync(cUser);
ApplicationUser userdetails = new ApplicationUser();
userdetails = dataController.GetUsersDetails(model.UserName);
if (userdetails.Email != null)
{
await ResetORForgotPasswordSendEmail(userdetails.FirstName, userdetails.UserName, userdetails.Email);
}
}
}
catch (Exception ex)
{
Logger.Error(ex.Message, ex);
}
return Ok();
}

VerifyHashedPassword is going to compare the password with the user's current password. What you should be doing instead is simply hash the new password and do a simple string compare to your list of old password hashes:
if (pwds.Contains(UserManager.PasswordHasher.HashPassword(model.NewPassword)))
{
ModelState.AddModelError("NewPassword", "You've used that password before.");
}

Related

Header Request too long in Error 400 on Chrome, but works fine on firefox

AccountController
This is my SignMeIn Claim function
private void SignMeIn(User user, bool isPersistent)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.Email, user.Email),
new Claim(Global.CustomClaimTypes.UserId, user.UserId),
new Claim(Global.CustomClaimTypes.UserType, user.UserType),
new Claim(Global.CustomClaimTypes.UserModules, JsonConvert.SerializeObject(user.Rights)),
// ISSUE 001 - ONLY ON CHROME. WORKS FINE ON FIREFOX! - 05/06/2022
// Too heavy claim for store. Causing ERROR 400, data requested from the server is too long.
// Comment the code below to remove claim request from session!
new Claim(Global.CustomClaimTypes.UserStores, JsonConvert.SerializeObject(user.Stores)) // Original Code
};
var id = new ClaimsIdentity(claims, Global.AuthenticationTypes.ApplicationCookie);
var ctx = Request.GetOwinContext();
var authenticationManager = ctx.Authentication;
authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, id);
}
I'm using claims to save all user data into its current session, and can easily access Global variables and functionalities.
It work's fine calling out in Login Function
public ActionResult Login(LoginViewModel model, string returnUrl)
{
try
{
if (ModelState.IsValid)
{
var user = _userService.VerifyUser(model.InputUserID, ClsRijndael.EncryptRijndael(model.InputPassword));
if (user != null)
{
SignMeIn(user, model.RememberMe);
// Calling the fetch function - 05/06/2022
FetchStore(user);
//_sessionService.Set<string>(Global.SessionKeys.UserImage, user.ImageData);
if (user.UserType == "PCP")
{
return RedirectToAction("Main", "PCP");
}
else if (user.UserType == "SIC")
{
return RedirectToAction("Main", "SIC");
}
else if (user.UserType == "CCP")
{
return RedirectToAction("Main", "CCP");
}
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
return View(model);
}
catch (AuthenticationException)
{
ModelState.AddModelError("", "There is a problem with your account, please contact your administrator.");
return View(model);
}
catch (System.Exception)
{
ModelState.AddModelError("", "Invalid username or password.");
return View(model);
}
}
But if I run on Chrome, it gives the Error 400 on specific that has quite a load of data.
CHROME ERROR
BUT IT WORKS FINE ON FIREFOX
Solutions I've tried:
clearing all the cookies and it doesn't seem to work.
adding a new claim outside the controller, but I haven't seen the fitting article for it so far.
web.server maxRequestLength = 'maximum amount'

ModelState.AddModelError is setup to display Canned message instead of real Error

In my website i had a group of people working on my site and i have this code that they put a canned message in for an error. When i debug the code it is actually a different error but displays this canned message. For instance when i put the information in the form i used an email address that already exists in the database but it is showing a message to check the password requirements. How can this be fixed to show the actual error. To me it also seems like there is a lot code going on in this that may not need to be or can be achieved cleaner Your thoughts?
Code of Post Action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateCompanyUser(ExpandedUserDTO ExpandedUserDTO)
{
try
{
if (ExpandedUserDTO == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var Email = ExpandedUserDTO.Email.Trim();
var UserName = ExpandedUserDTO.UserName.Trim();
var Password = ExpandedUserDTO.Password.Trim();
if (UserName == "")
{
throw new Exception("No Username");
}
if (Password == "")
{
throw new Exception("No Password");
}
// UserName is LowerCase of the Email
// UserName = Email.ToLower();
// Create user
var objNewAdminUser = new Models.ApplicationUser { UserName = UserName, Email = Email };
var AdminUserCreateResult = UserManager.Create(objNewAdminUser, Password);
if (AdminUserCreateResult.Succeeded == true)
{
string strNewRole = Convert.ToString(Request.Form["Roles"]);
if (strNewRole != "0")
{
// Put user in role
UserManager.AddToRole(objNewAdminUser.Id, strNewRole);
}
var viewModel = new Users();
{
viewModel.UsersId = Convert.ToString(Guid.NewGuid());
viewModel.Email = Email;
viewModel.FirstName = UserName;
viewModel.AspNetUsersId = objNewAdminUser.Id;
viewModel.CreatedDate = System.DateTime.Now;
viewModel.UpdatedDate = System.DateTime.Now;
};
UsersBusinessModels Login = new UsersBusinessModels();
var results = Login.insertUserWithougAsny(viewModel);
string[] roleRemove = new string[2] { "Administrator", "CompanyAdmin" };
ViewBag.Roles = GetAllRolesAsSelectList().Where(k => !roleRemove.Contains(k.Text)).ToList();
// return RedirectToAction();
Response.Redirect("/Customer/ManageUsers/" + User.Identity.GetUserId());
return PartialView();
}
else
{
ViewBag.Roles = GetAllRolesAsSelectList();
ModelState.AddModelError(string.Empty,
"Error: Failed to create the user. Check password requirements.");
return PartialView(ExpandedUserDTO);
}
}
catch (Exception ex)
{
ViewBag.Roles = GetAllRolesAsSelectList();
ModelState.AddModelError(string.Empty, "Error: " + ex);
string[] roleRemove = new string[2] { "Administrator", "CompanyAdmin" };
ViewBag.Roles = GetAllRolesAsSelectList().Where(k => !roleRemove.Contains(k.Text)).ToList();
return PartialView(ExpandedUserDTO);
}
}

How to pass a claim in a cookie in ASP.NET Identity?

I am trying to save a Guid value in a cookie. The way I want to save it is by setting a Claim for the user and getting it back in every controller for validation purposes.
Before going on the subject I have seen this question here but it did not solve my problem!
Here is my code in login controller:
public async Task<ActionResult> Login_Post(LoginViewModel loginViewModel)
{
if (ModelState.IsValid)
{
var user = IdentityManager.UserManager.FindByName(loginViewModel.Username);
if (user != null)
{
IdentityManager.SignInManager.AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var status = SignInStatus.Failure;
if (!user.IsApproved)
{
status = SignInStatus.Failure;
}
if (!await IdentityManager.UserManager.IsEmailConfirmedAsync(user.Id))
{
status = SignInStatus.EmailNotConfirmed;
}
status = (SignInStatus)await IdentityManager.SignInManager
.PasswordSignInAsync(loginViewModel.Username, loginViewModel.Password, loginViewModel.RememberMe, true);
if (status == SignInStatus.Failure)
{
await IdentityManager.UserManager.AccessFailedAsync(user.Id);
if (await IdentityManager.UserManager.IsLockedOutAsync(user.Id))
{
status = SignInStatus.LockedOut;
}
}
switch (status)
{
case SignInStatus.Success:
var identity = await IdentityManager.UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
var permissions = IdentityManager.ApplicationDbContext.UserVesselPermissions(Guid.Parse(identity.GetUserId()), DateTime.Now).ToList();
var userVesselPermissionsResult = permissions.FirstOrDefault();
identity.AddClaim(userVesselPermissionsResult != null
? new Claim(SealogicalClaimUtility.VesselId, userVesselPermissionsResult.VesselId.ToString())
: new Claim(SealogicalClaimUtility.VesselId, ""));
IdentityManager.SignInManager.AuthenticationManager.SignIn(identity);
return RedirectToAction("Index", "HomeDashboard", new {area = "Dashboard"});
case SignInStatus.Failure:
ModelState.AddModelError("", "Login failed, username or password is incorrect.");
break;
case SignInStatus.RequiresVerification:
ModelState.AddModelError("", "Your account needs verification");
break;
case SignInStatus.LockedOut:
ModelState.AddModelError("",
"Login failed, this account has been locked. Contact administrator for further information.");
break;
case SignInStatus.EmailNotConfirmed:
ModelState.AddModelError("",
"Login failed, activate this account by clicking the email sent to your email account.");
break;
default:
ModelState.AddModelError("",
"Login failed, unkwon error. Contact administrator for further information");
break;
}
}
}
return View("Login", loginViewModel);
}
I am using PasswordSignInAsync() method for login validation because I need to get back that SignInStatus in order show a proper message to the user.
After that I read this question in here and they say to create a ClaimsIdentity and then use again the AuthenticationManager to sign in with that ClaimsIdentity object as I did here:
case SignInStatus.Success:
var identity = await IdentityManager.UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
var permissions = IdentityManager.ApplicationDbContext.UserVesselPermissions(Guid.Parse(identity.GetUserId()), DateTime.Now).ToList();
var userVesselPermissionsResult = permissions.FirstOrDefault();
identity.AddClaim(userVesselPermissionsResult != null
? new Claim(SealogicalClaimUtility.VesselId, userVesselPermissionsResult.VesselId.ToString())
: new Claim(SealogicalClaimUtility.VesselId, ""));
IdentityManager.SignInManager.AuthenticationManager.SignIn(identity);
return RedirectToAction("Index", "HomeDashboard", new {area = "Dashboard"});
SealogicalClaimUtility as a static class as below:
public static class SealogicalClaimUtility
{
public const string VesselId = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/vesselid";
}
Until now everything is working fine but when I try to get back that Claim it does not exist at all.
To get back that clam I use a custom property as below:
public static Guid? UserSelectedVesselId
{
get
{
var hasVesselClaim = ((ClaimsIdentity) HttpContext.Current.User.Identity)
.HasClaim(claim => claim.Type == SealogicalClaimUtility.VesselId);
if (hasVesselClaim)
{
var vesselClaim = ((ClaimsIdentity) HttpContext.Current.User.Identity).Claims.FirstOrDefault(
claim => claim.Type == SealogicalClaimUtility.VesselId);
if (vesselClaim != null)
{
Guid vesselId;
if (Guid.TryParse(vesselClaim.Value, out vesselId))
{
return vesselId;
}
}
return null;
}
var currentUserPermission = UserVesselPermissions.FirstOrDefault();
return currentUserPermission?.VesselId;
}
}
Any idea what I am doing wrong in here ?
Thanks in advance!
I am using ASP.NET Identity version 2

Bcrypt password verifying

I'm trying to decrypt the password on the login method, but it allows to login with any password I type in, not sure why, maybe someone could help me out?
My login method in the db layer:
public string loginUser(string userName, string pass)
{
string result = "";
try
{
var mongoClient = new MongoClient("mongodb://localhost");
var database = mongoClient.GetDatabase("SearchForKnowledge");
var coll = database.GetCollection<BsonDocument>("Users");
var filter = Builders<BsonDocument>.Filter.Eq("userName", userName);
var results = coll.Find(filter).ToList().First();
if (BCrypt.Net.BCrypt.Verify(pass, results["password"].ToString()))
{
result = results["userName"].ToString();
}
}
catch (Exception ex)
{
result = "";
}
return result;
}
My user controller:
public ActionResult Login(UsersLogin form)
{
User user = new User();
UserDB udb = new UserDB();
if (!form.Username.IsEmpty())
{
udb.loginUser(form.Username, form.Password);
Session["userName"] = form.Username;
return RedirectToRoute("Home");
}
return RedirectToRoute("Login");
}
The problem is in your controller
udb.loginUser(form.Username, form.Password);
Session["userName"] = form.Username;
return RedirectToRoute("Home");
You call udb.loginUser(form.Username, form.Password);, but you never check the return value of udb.loginUser method, so your code will always redirect to the home page no matter what the user name and password are.
Based on the code of loginUser method, it will return an empty string if the login fails, so change the above three lines of code to below
var loginResult = udb.loginUser(form.Username, form.Password);
if (!string.IsNullOrEmpty(loginResult))
{
Session["userName"] = form.Username;
return RedirectToRoute("Home");
}

Forget password form in asp.net mvc 4

I try to implement forget password form in my asp.net mvc 4 project, everything works fine, but when I try to login to system with new password it told me that I have wrong password.
[HttpPost]
public ActionResult ForgetPassword(UserViewModel userModel) {
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var random = new Random();
var result = new string(
Enumerable.Repeat(chars, 8)
.Select(s => s[random.Next(s.Length)])
.ToArray());
User user = _userRepo.GetUserByEmail(userModel.Email);
if (user == null) {
ViewBag.Error = Resources.Account.userEmailNotExist;
return View(userModel);
}
String newHashedPassword = Crypto.HashPassword(result);
user.Password = newHashedPassword;
user.LastPasswordChangedDate = DateTime.UtcNow;
_userRepo.SaveChanges();
string enMessage = "Your new password: " + result;
var httpCookie = Request.Cookies["lang"];
if (httpCookie != null && httpCookie.Value == "en") {
_mailHelper.SendEmail(userModel.Email, "New password", enMessage);
}
return RedirectToAction("ConfirmPasswordChange", "Account");
}
Login form:
[HttpPost]
public ActionResult Login(UserViewModel user) {
var users = _userRepo.GetAllEntitiesWithParam("JobsDb_Users_GetByEmail", user.Email).FirstOrDefault();
...
try {
var tryLogin = WebSecurity.Login(users.Username, user.Password, true);
if (tryLogin == WebSecurity.MembershipLoginStatus.Failure)
{
var httpCookie = Request.Cookies["lang"];
if (httpCookie != null && httpCookie.Value == "en") {
ViewBag.Error = "Your password is incorrect.";
new SeoHelper().ReturnSeoTags(this, "Login");
}
return View(user);
}
...
} catch {
...
}
}
inside WebSecurity
public static MembershipLoginStatus Login(string username, string password, bool rememberMe) {
if (Membership.ValidateUser(username, password)) {
FormsAuthentication.SetAuthCookie(username, rememberMe);
return MembershipLoginStatus.Success;
} else {
return MembershipLoginStatus.Failure;
}
}
inside Membership
public override bool ValidateUser(string username, string password) {
if (string.IsNullOrEmpty(username)) {
return false;
}
if (string.IsNullOrEmpty(password)) {
return false;
}
User user = _userRepository.GetAll().FirstOrDefault(usr => usr.Username == username);
if (user == null) {
return false;
}
if (!user.IsApproved.Value) {
return false;
}
if (user.IsLockedOut.Value) {
return false;
}
String hashedPassword = user.Password;
Boolean verificationSucceeded = (hashedPassword != null && Crypto.VerifyHashedPassword(hashedPassword, password));
if (verificationSucceeded) { //here is I have false if try to login using password from forget form
user.PasswordFailuresSinceLastSuccess = 0;
user.LastLoginDate = DateTime.UtcNow;
user.LastActivityDate = DateTime.UtcNow;
} else {
int failures = user.PasswordFailuresSinceLastSuccess.Value;
if (failures < MaxInvalidPasswordAttempts) {
user.PasswordFailuresSinceLastSuccess += 1;
user.LastPasswordFailureDate = DateTime.UtcNow;
} else if (failures >= MaxInvalidPasswordAttempts) {
user.LastPasswordFailureDate = DateTime.UtcNow;
user.LastLockoutDate = DateTime.UtcNow;
user.IsLockedOut = true;
}
}
_userRepository.SaveChanges();
if (verificationSucceeded) {
return true;
}
return false;
}
First step is to open up your database and verify that the new password was actually persisted. If it has, the most likely cause is that your repository is working with stale (cached) data.
If you're using Entity Framework, this happens because the framework will, by default, cache the state of the database at the time the DbContext is created, so it is retaining your original password. You can verify this by logging in with the original password.
I am not sure but following code does not look right to me:
User user = _userRepo.GetUserByEmail(userModel.Email);
if (user == null) {
ViewBag.Error = Resources.Account.userEmailNotExist;
return View(userModel);
}
String newHashedPassword = Crypto.HashPassword(result);
user.Password = newHashedPassword;
user.LastPasswordChangedDate = DateTime.UtcNow;
_userRepo.SaveChanges();
You fetched the user from repository, make changes to user object in memory and then called SaveChanges() on the repository. Does that work in your world? How does _userRepo.SaveChanges(); knows which object has changed. Do you see correct hashed value in DB after the call? What value you see in ValidateUser() method for password? Is the hashing algorithm consistent both while generating hashed password and while verifying?
I could be wrong, if that's the case it will be good if you share little bit more of analysis around the question I asked above.

Categories

Resources