Added more validation to the various AuthController functions.

This commit is contained in:
2021-01-15 09:41:35 -06:00
parent 8e9824f497
commit 5a9d3621fe
4 changed files with 68 additions and 15 deletions
@@ -47,7 +47,6 @@ namespace SecureCore.Authentication
public static void InsertPasswordResetRequest(string email, string sessionToken, DateTime expirationDate, string userAgent, string ipAddress, string connectionString) 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);
using var command = new SqlCommand("LogPasswordResetRequest", connection) { CommandType = CommandType.StoredProcedure }; using var command = new SqlCommand("LogPasswordResetRequest", connection) { CommandType = CommandType.StoredProcedure };
command.Parameters.AddWithValue("SessionToken", sessionToken); command.Parameters.AddWithValue("SessionToken", sessionToken);
@@ -78,7 +77,6 @@ namespace SecureCore.Authentication
public static (string PasswordHash, string Salt) GetPasswordHashAndSalt(string username, string connectionString) public static (string PasswordHash, string Salt) GetPasswordHashAndSalt(string username, string connectionString)
{ {
using var connection = new SqlConnection(connectionString); using var connection = new SqlConnection(connectionString);
using var command = new SqlCommand("SELECT [Password Hash], [Salt] FROM dbo.GetUserPasswordHashAndSalt(@Username)", connection); using var command = new SqlCommand("SELECT [Password Hash], [Salt] FROM dbo.GetUserPasswordHashAndSalt(@Username)", connection);
command.Parameters.AddWithValue("Username", username); command.Parameters.AddWithValue("Username", username);
@@ -20,7 +20,6 @@ namespace SecureCore.Authentication
public static bool IsSessionTokenValid(string sessionToken, string connectionString, bool isResetToken = false) public static bool IsSessionTokenValid(string sessionToken, string connectionString, bool isResetToken = false)
{ {
using var connection = new SqlConnection(connectionString); using var connection = new SqlConnection(connectionString);
using var command = new SqlCommand("ValidateSessionToken", connection) { CommandType = CommandType.StoredProcedure }; using var command = new SqlCommand("ValidateSessionToken", connection) { CommandType = CommandType.StoredProcedure };
command.Parameters.AddWithValue("SessionToken", sessionToken); command.Parameters.AddWithValue("SessionToken", sessionToken);
@@ -36,7 +35,6 @@ namespace SecureCore.Authentication
public static void Logout(string sessionToken, string connectionString) public static void Logout(string sessionToken, string connectionString)
{ {
using var connection = new SqlConnection(connectionString); using var connection = new SqlConnection(connectionString);
using var command = new SqlCommand("LogoutUser", connection) { CommandType = CommandType.StoredProcedure }; using var command = new SqlCommand("LogoutUser", connection) { CommandType = CommandType.StoredProcedure };
command.Parameters.AddWithValue("SessionToken", sessionToken); command.Parameters.AddWithValue("SessionToken", sessionToken);
@@ -49,7 +47,6 @@ namespace SecureCore.Authentication
public static void Login(string userName, string sessionToken, DateTime expirationDate, string userAgent, string ipAddress, string connectionString) public static void Login(string userName, string sessionToken, DateTime expirationDate, string userAgent, string ipAddress, string connectionString)
{ {
using var connection = new SqlConnection(connectionString); using var connection = new SqlConnection(connectionString);
using var command = new SqlCommand("LoginUser", connection) { CommandType = CommandType.StoredProcedure }; using var command = new SqlCommand("LoginUser", connection) { CommandType = CommandType.StoredProcedure };
command.Parameters.AddWithValue("SessionToken", sessionToken); command.Parameters.AddWithValue("SessionToken", sessionToken);
+35 -8
View File
@@ -17,7 +17,7 @@ namespace SecureCore.Controllers
public IActionResult Login([FromBody] UserInformation.LoginData info) public IActionResult Login([FromBody] UserInformation.LoginData info)
{ {
AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString); AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString);
//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 (HttpContext.Request.Cookies.ContainsKey("Session"))
@@ -27,6 +27,10 @@ namespace SecureCore.Controllers
//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);
//Now make sure the password is valid.
result = PasswordManager.IsPasswordValid(info.Password);
if (!result.IsValid) return Unauthorized(result.Message); if (!result.IsValid) return Unauthorized(result.Message);
//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.
@@ -34,10 +38,6 @@ namespace SecureCore.Controllers
//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);
if (!result.IsValid) return Unauthorized(result.Message);
if(!PasswordManager.IsPasswordAMatch(info.Password, salt, password)) return Unauthorized("User name or password is not correct."); if(!PasswordManager.IsPasswordAMatch(info.Password, salt, password)) return Unauthorized("User name or password is not correct.");
var sessionToken = SessionManager.CreateSessionToken(); var sessionToken = SessionManager.CreateSessionToken();
@@ -62,6 +62,7 @@ namespace SecureCore.Controllers
[AcceptVerbs("POST")] [AcceptVerbs("POST")]
public IActionResult Register([FromBody] UserInformation.RegistrationData info) public IActionResult Register([FromBody] UserInformation.RegistrationData info)
{ {
AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString);
//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.
@@ -71,7 +72,12 @@ namespace SecureCore.Controllers
if (!result.IsValid) return Unauthorized(result.Message); if (!result.IsValid) return Unauthorized(result.Message);
AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString); result = UserDataService.IsEmailValid(info.Email);
if (!result.IsValid) return Unauthorized(result.Message);
if (UserDataService.IsEmailInUse(info.Email, connectionString)) return Unauthorized("This email is already in use.");
var (hash, salt) = PasswordManager.HashPassword(info.Password); var (hash, salt) = PasswordManager.HashPassword(info.Password);
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];
@@ -97,9 +103,18 @@ namespace SecureCore.Controllers
[AcceptVerbs("POST")] [AcceptVerbs("POST")]
public IActionResult Logout() public IActionResult Logout()
{ {
if (!HttpContext.Request.Cookies.ContainsKey("Session")) return Ok();
AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString); AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString);
SessionManager.Logout(HttpContext.Request.Cookies["Session"], connectionString); try
{
SessionManager.Logout(HttpContext.Request.Cookies["Session"], connectionString);
}
catch(Exception e)
{
//TODO: decide what to do here. If the SQL fails to clear the token from the database, do we want to clear the token cookie?
}
HttpContext.Response.Cookies.Delete("Session"); HttpContext.Response.Cookies.Delete("Session");
@@ -114,7 +129,11 @@ namespace SecureCore.Controllers
try try
{ {
if (!SessionManager.IsSessionTokenValid(token, connectionString, true)) return Unauthorized("Token invalid"); if (!SessionManager.IsSessionTokenValid(token, connectionString, true)) return Unauthorized("This link has expired, please request a new email reset link.");
var (IsValid, Message) = PasswordManager.IsPasswordValid(password);
if (!IsValid) return Unauthorized(Message);
var (Hash, Salt) = PasswordManager.HashPassword(password); var (Hash, Salt) = PasswordManager.HashPassword(password);
@@ -133,6 +152,14 @@ namespace SecureCore.Controllers
public IActionResult CreatePasswordResetLink([FromBody] string email) public IActionResult CreatePasswordResetLink([FromBody] string email)
{ {
AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString); AppSettingsManager.TryGetConnectionStringByName("MainDataConnectionString", out string connectionString);
//Validate the email input.
var (IsValid, Message) = UserDataService.IsEmailValid(email);
if (!IsValid) return Unauthorized(Message);
//If its a properly formatted email, then check to see if its in use by anyone.
if (!UserDataService.IsEmailInUse(email, connectionString)) return Unauthorized("Email is not valid.");
var token = SessionManager.CreateSessionToken(); var token = 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()); var ip = PasswordManager.HashStringData(Request.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString());
+32 -1
View File
@@ -11,6 +11,7 @@ namespace SecureCore.Services
{ {
// //
public static int UserNameMaxLength { get; } = 64; public static int UserNameMaxLength { get; } = 64;
public static int EmailMaxLength { get; } = 512;
public static (bool IsValid, string Message) IsUsernameValid(string userName) public static (bool IsValid, string Message) IsUsernameValid(string userName)
{ {
@@ -29,7 +30,6 @@ namespace SecureCore.Services
public static int RegisterNewUser(string userName, string email, string passwordHash, string saltHash, string sessionToken, DateTime expirationDate, string userAgent, string ipAddress, string connectionString) public static int RegisterNewUser(string userName, string email, string passwordHash, string saltHash, string sessionToken, DateTime expirationDate, string userAgent, string ipAddress, string connectionString)
{ {
using var connection = new SqlConnection(connectionString); using var connection = new SqlConnection(connectionString);
using var command = new SqlCommand("RegisterNewUser", connection) { CommandType = CommandType.StoredProcedure }; using var command = new SqlCommand("RegisterNewUser", connection) { CommandType = CommandType.StoredProcedure };
command.Parameters.AddWithValue("UserName", userName); command.Parameters.AddWithValue("UserName", userName);
@@ -47,5 +47,36 @@ namespace SecureCore.Services
return Convert.ToInt32(result); return Convert.ToInt32(result);
} }
public static bool IsEmailInUse(string email, string connectionString)
{
using var connection = new SqlConnection(connectionString);
using var command = new SqlCommand("SELECT dbo.EmailInUse(@Email) AS InUse", connection);
command.Parameters.AddWithValue("Email", email);
connection.Open();
return Convert.ToBoolean(command.ExecuteScalar());
}
public static (bool IsValid, string Message) IsEmailValid(string email)
{
if (email.Length > EmailMaxLength) return (false, $"Email exceeds the maximum length of {EmailMaxLength} characters.");
//Code taken from user "Cogwheel" on StackOverflow: https://stackoverflow.com/questions/1365407/c-sharp-code-to-validate-email-address
try
{
var addr = new System.Net.Mail.MailAddress(email);
if (addr.Address != email) return (false, $"The provided email is not in the correct format.");
return (true, string.Empty);
}
catch
{
return (false, $"The provided email is not in the correct format.");
}
}
} }
} }