Added and tested the password reset feature. So far so good, needs some more validation but its working and that's a good first step.

This commit is contained in:
2021-01-13 16:41:20 -06:00
parent e198838a32
commit 6f8b7af78e
3 changed files with 51 additions and 23 deletions
Binary file not shown.
+24 -10
View File
@@ -29,20 +29,22 @@ namespace SecureCore.Authentication
return (GetHash(password, salt), Convert.ToBase64String(salt)); return (GetHash(password, salt), Convert.ToBase64String(salt));
} }
public static (bool IsValid, string Message) IsPasswordValid(string password, string salt, string passwordHash) public static (bool IsValid, string Message) IsPasswordValid(string password)
{ {
if (password.Length < MinPasswordLength) return (false, $"Your password is too short, it must be at least {MinPasswordLength} characters long and not exceed {MaxPasswordLength} characters."); if (password.Length < MinPasswordLength) return (false, $"Your password is too short, it must be at least {MinPasswordLength} characters long and not exceed {MaxPasswordLength} characters.");
if (password.Length > MaxPasswordLength) return (false, $"Your password is too long, it must not exceed {MaxPasswordLength} characters and must contain at least {MinPasswordLength} characters."); if (password.Length > MaxPasswordLength) return (false, $"Your password is too long, it must not exceed {MaxPasswordLength} characters and must contain at least {MinPasswordLength} characters.");
var saltBytes = Convert.FromBase64String(salt); return (true, string.Empty);
if (passwordHash == GetHash(password, saltBytes))
return (true, string.Empty);
else
return (false, "User name or password is not correct.");
} }
public static void InsertPasswordResetRequest(string userName, string sessionToken, DateTime expirationDate, string userAgent, string ipAddress, string connectionString) public static bool IsPasswordAMatch(string password, string salt, string passwordHash)
{
var saltBytes = Convert.FromBase64String(salt);
return passwordHash == GetHash(password, saltBytes);
}
public static void InsertPasswordResetRequest(string email, string sessionToken, DateTime expirationDate, string userAgent, string ipAddress, string connectionString)
{ {
using var connection = new SqlConnection(connectionString); using var connection = new SqlConnection(connectionString);
@@ -50,7 +52,7 @@ namespace SecureCore.Authentication
command.Parameters.AddWithValue("SessionToken", sessionToken); command.Parameters.AddWithValue("SessionToken", sessionToken);
command.Parameters.AddWithValue("ExpirationDate", expirationDate); command.Parameters.AddWithValue("ExpirationDate", expirationDate);
command.Parameters.AddWithValue("UserName", userName); command.Parameters.AddWithValue("Email", email);
command.Parameters.AddWithValue("UserAgent", userAgent); command.Parameters.AddWithValue("UserAgent", userAgent);
command.Parameters.AddWithValue("IpAddress", ipAddress); command.Parameters.AddWithValue("IpAddress", ipAddress);
@@ -59,7 +61,19 @@ namespace SecureCore.Authentication
command.ExecuteNonQuery(); command.ExecuteNonQuery();
} }
//TODO: Reset Password Function Here public static void ResetPassword(string passwordHash, string salt, string sessionToken, string connectionString)
{
using var connection = new SqlConnection(connectionString);
using var command = new SqlCommand("ResetPassword", connection) { CommandType = CommandType.StoredProcedure };
command.Parameters.AddWithValue("PasswordHash", passwordHash);
command.Parameters.AddWithValue("Salt", salt);
command.Parameters.AddWithValue("SessionToken", sessionToken);
connection.Open();
command.ExecuteNonQuery();
}
public static (string PasswordHash, string Salt) GetPasswordHashAndSalt(string username, string connectionString) public static (string PasswordHash, string Salt) GetPasswordHashAndSalt(string username, string connectionString)
{ {
+27 -13
View File
@@ -23,25 +23,26 @@ namespace SecureCore.Controllers
//TODO: see if the request has an active session key so we don't spam out new keys like mad //TODO: see if the request has an active session key so we don't spam out new keys like mad
try try
{ {
if (HttpContext.Request.Cookies.ContainsKey("Session"))
if (SessionManager.IsSessionTokenValid(HttpContext.Request.Cookies["Session"], connectionString))
return Ok("Logged in\n");
//Verify that the username provided is valid, i.e. no whitespace, special characters, etc. //Verify that the username provided is valid, i.e. no whitespace, special characters, etc.
var result = UserDataService.IsUsernameValid(info.UserName); var result = UserDataService.IsUsernameValid(info.UserName);
//If the name isn't valid, return the error message to the user. //If the name isn't valid, return the error message to the user.
if (!result.IsValid) return Unauthorized(result.Message); if (!result.IsValid) return Unauthorized(result.Message);
//if (HttpContext.Request.Cookies.ContainsKey("Session"))
//{
// if(UserDataService.IsSessionTokenValid(HttpContext.Request.Cookies["Session"]))//, info.UserName))
// return Ok($"Session is live{Environment.NewLine}");
//}
//Next try to get the user's login data, if the fuction returns empty strings, then the user isn't a registered name. //Next try to get the user's login data, if the fuction returns empty strings, then the user isn't a registered name.
var (password, salt) = PasswordManager.GetPasswordHashAndSalt(info.UserName, connectionString); var (password, salt) = PasswordManager.GetPasswordHashAndSalt(info.UserName, connectionString);
//If the user name isn't in the system, then simply return a generic error message about something not being right. //If the user name isn't in the system, then simply return a generic error message about something not being right.
if (password == string.Empty) return Unauthorized("User name or password is not correct."); if (password == string.Empty) return Unauthorized("User name or password is not correct.");
result = PasswordManager.IsPasswordValid(info.Password, salt, password); result = PasswordManager.IsPasswordValid(info.Password);
if (!result.IsValid) return Unauthorized(result.Message); if (!result.IsValid) return Unauthorized(result.Message);
if(!PasswordManager.IsPasswordAMatch(info.Password, salt, password)) return Unauthorized("User name or password is not correct.");
var sessionToken = SessionManager.CreateSessionToken(); var sessionToken = SessionManager.CreateSessionToken();
var agent = HttpContext.Request.Headers[Microsoft.Net.Http.Headers.HeaderNames.UserAgent]; var agent = HttpContext.Request.Headers[Microsoft.Net.Http.Headers.HeaderNames.UserAgent];
var ip = PasswordManager.HashStringData(Request.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString(), salt); var ip = PasswordManager.HashStringData(Request.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString(), salt);
@@ -64,6 +65,15 @@ namespace SecureCore.Controllers
[AcceptVerbs("POST")] [AcceptVerbs("POST")]
public IActionResult Register([FromBody] LoginInfo info) public IActionResult Register([FromBody] LoginInfo info)
{ {
//Verify that the username provided is valid, i.e. no whitespace, special characters, etc.
var result = UserDataService.IsUsernameValid(info.UserName);
//If the name isn't valid, return the error message to the user.
if (!result.IsValid) return Unauthorized(result.Message);
//Validate the password, make sure its not to long or short, etc.
result = PasswordManager.IsPasswordValid(info.Password);
if (!result.IsValid) return Unauthorized(result.Message);
AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString); AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString);
var (hash, salt) = PasswordManager.HashPassword(info.Password); var (hash, salt) = PasswordManager.HashPassword(info.Password);
var sessionToken = SessionManager.CreateSessionToken(); var sessionToken = SessionManager.CreateSessionToken();
@@ -99,9 +109,9 @@ namespace SecureCore.Controllers
return Ok(); return Ok();
} }
[HttpGet("ResetPassword")] [HttpPost("ResetPassword")]
//[AcceptVerbs("GET")] [AcceptVerbs("POST")]
public IActionResult ResetPassword([FromQuery] string token) public IActionResult ResetPassword([FromQuery] string token, [FromBody] string password)
{ {
AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString); AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString);
@@ -109,17 +119,21 @@ namespace SecureCore.Controllers
{ {
if (!SessionManager.IsSessionTokenValid(token, connectionString, true)) return Unauthorized("Token invalid"); if (!SessionManager.IsSessionTokenValid(token, connectionString, true)) return Unauthorized("Token invalid");
var (Hash, Salt) = PasswordManager.HashPassword(password);
PasswordManager.ResetPassword(Hash, Salt, token, connectionString);
return Ok("Reset successful\n");
} }
catch (Exception e) catch (Exception e)
{ {
return Unauthorized(e.Message);
} }
return Ok("Done");
} }
[HttpPost("CreatePasswordResetLink")] [HttpPost("CreatePasswordResetLink")]
[AcceptVerbs("POST")] [AcceptVerbs("POST")]
public IActionResult CreatePasswordResetLink([FromBody] string userName) //TODO: this sig should only accept an email, so the link can be sent there. public IActionResult CreatePasswordResetLink([FromBody] string email) //TODO: this sig should only accept an email, so the link can be sent there.
{ {
AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString); AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString);
var token = SessionManager.CreateSessionToken(); var token = SessionManager.CreateSessionToken();
@@ -128,7 +142,7 @@ namespace SecureCore.Controllers
try try
{ {
PasswordManager.InsertPasswordResetRequest(userName, token, DateTime.Now.AddHours(1), agent, ip, connectionString); PasswordManager.InsertPasswordResetRequest(email, token, DateTime.Now.AddHours(1), agent, ip, connectionString);
token = HttpUtility.UrlEncode(token); token = HttpUtility.UrlEncode(token);