Added more validation to the various AuthController functions.
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user