Files
secure-core/SecureCore/Controllers/AuthController.cs
T

197 lines
8.8 KiB
C#

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using System;
using System.Web;
using SecureCore.Services;
using SecureCore.Authentication;
using SecureCore.Models;
namespace SecureCore.Controllers
{
[Route("[controller]")]
[ApiController]
public class AuthController : Controller
{
public static string BaseUrl { get; set; }
//TODO: Login will only ever return messages like "Wrong username / password." whereas register can return messages like "User exists.", "Password to weak", or "Password in top 100 most used.".
[HttpPost("login")]
[AcceptVerbs("POST")]
public IActionResult Login([FromBody] LoginInfo info)
{
AppSettingsManager.TryGetConnectionString("MainDataConnectionString", out string connectionString);
try
{
if (SessionManager.IsSessionTokenValid(HttpContext))
return Ok("Logged in\n");
//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);
//Now make sure the password is valid.
result = PasswordManager.IsPasswordValid(info.Password);
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.
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 (password == string.Empty) 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 agent = HttpContext.Request.Headers[Microsoft.Net.Http.Headers.HeaderNames.UserAgent];
var ip = PasswordManager.HashStringData(Request.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString(), salt);
var cookieOptions = GetCookieOptions();
SessionManager.Login(info.Username, sessionToken, cookieOptions.Expires.Value.UtcDateTime, agent, ip, connectionString);
HttpContext.Response.Cookies.Append(SessionManager.SessionCookieName, sessionToken, cookieOptions);
return Ok($"Logged in success{Environment.NewLine}");
}
catch(Exception ex)
{
//TODO: Log this event.
return Unauthorized("An error has occured trying to process your request, please try again in a few minutes.");
}
}
[HttpPost("Register")]
[AcceptVerbs("POST")]
public IActionResult Register(RegistrationInfo info)
{
AppSettingsManager.TryGetConnectionString("MainDataConnectionString", out string connectionString);
//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);
result = UserDataService.IsEmailValid(info.Email);
if (!result.IsValid) return Unauthorized(result.Message);
try
{
var (hash, salt) = PasswordManager.HashPassword(info.Password);
var sessionToken = SessionManager.CreateSessionToken();
var agent = HttpContext.Request.Headers[Microsoft.Net.Http.Headers.HeaderNames.UserAgent];
var ip = PasswordManager.HashStringData(Request.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString(), salt);
var cookieOptions = GetCookieOptions();
var i = UserDataService.RegisterNewUser(info.Username, info.Email, hash, salt, sessionToken, cookieOptions.Expires.Value.UtcDateTime, agent, ip, connectionString);
HttpContext.Response.Cookies.Append(SessionManager.SessionCookieName, sessionToken, cookieOptions);
return Ok($"New User ID: {i}");
}
catch (Exception e)
{
//TODO: redo the message that sent back to the client. There could be more than just a SQL Server error here.
return Unauthorized(e.Message);
}
}
[HttpPost("Logout")]
[AcceptVerbs("POST")]
public IActionResult Logout()
{
if (!HttpContext.Request.Cookies.ContainsKey("Session")) return Ok();
AppSettingsManager.TryGetConnectionString("MainDataConnectionString", out string connectionString);
try
{
SessionManager.Logout(HttpContext.Request.Cookies[SessionManager.SessionCookieName], 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(SessionManager.SessionCookieName);
return Ok();
}
[HttpPost("ResetPassword")]
[AcceptVerbs("POST")]
public IActionResult ResetPassword([FromQuery] string token, [FromBody] string password)
{
AppSettingsManager.TryGetConnectionString("MainDataConnectionString", out string connectionString);
try
{
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);
PasswordManager.ResetPassword(Hash, Salt, token, connectionString);
return Ok("Reset successful\n");
}
catch (Exception e)
{
return Unauthorized(e.Message);
}
}
[HttpPost("CreatePasswordResetLink")]
[AcceptVerbs("POST")]
public IActionResult CreatePasswordResetLink([FromBody] string email)
{
AppSettingsManager.TryGetConnectionString("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 agent = HttpContext.Request.Headers[Microsoft.Net.Http.Headers.HeaderNames.UserAgent];
var ip = PasswordManager.HashStringData(Request.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString());
try
{
PasswordManager.InsertPasswordResetRequest(email, token, DateTime.Now.AddHours(1), agent, ip, connectionString);
token = HttpUtility.UrlEncode(token);
//TODO: Allow the admin to configure the address that this function creates when doing password resets.
return Ok($"192.168.255.200:5000/auth/ResetPassword?token={token}{Environment.NewLine}");
}
catch(Exception e)
{
return Unauthorized($"{e.Message}{Environment.NewLine}");
}
}
private CookieOptions GetCookieOptions()
{
return new CookieOptions
{
//Domain = "copyrightcrusader.org",
Expires = DateTime.Now.AddDays(7),
//HttpOnly = true,
//Secure = true,
Path = "/",
SameSite = SameSiteMode.Strict
};
}
}
//TODO: Read more https://www.valentinog.com/blog/cookies/
// And this https://blog.webf.zone/ultimate-guide-to-http-cookies-2aa3e083dbae
}