public async Task <Web_AuthWrapper> LoginUser(string token, string username, string password) { var ret = new Web_AuthWrapper(); await AppDbContext.WithContext(async db => { if (username == null || password == null) { throw new ArgumentNullException("username or password was null"); } //Make sure the submitted hash isn't the same as the database hash. //The whole goal is to prevent reversing the hash and getting the original //password, so adding this and then doing an SHA256 makes it harder to do that. password = AuthHelper.GetDoubleHashedPassword(password); //Rate limit brute force attempts. await bruteForceLock.WaitAsync(); try { await Task.Delay(new Random().Next(25) + 100); //No guessing things from the timeout period. } finally { bruteForceLock.Release(); } //Get actual work done: username = username.ToUpper().Trim(); var user = await db.Users.FirstOrDefaultAsync(x => x.Email.ToUpper() == username); if (user == null) { return; } if (user.ResetPassword) { user.PasswordHash = password; user.ResetPassword = false; await db.SaveChangesAsync(); } if (user.PasswordHash != password) { return; } else { var now = DateTimeOffset.Now; if (user.SessionTokenExpiresUtc < now || user.SessionToken == null) { user.SessionToken = ""; var r = RandomNumberGenerator.Create(); for (var i = 0; i < 4; i++) { var cryptoBytes = new byte[4]; r.GetNonZeroBytes(cryptoBytes); var num = 0; for (int j = 0; j < cryptoBytes.Length; j++) { num += (cryptoBytes[j] << (j * 8)); } user.SessionToken += num.ToString(); } user.SessionTokenExpiresUtc = now.AddDays(30); await db.SaveChangesAsync(); } ret.token = user.SessionToken; ret.id = user.Id; ret.name = user.Name; ret.isAdmin = user.IsAdmin; } }); return(ret); }