using Microsoft.AspNetCore.Mvc; using BoredCareers.Services; using BoredCareers.Services.DatabaseService; using BoredCareers.Entities; using System.Web.Http; namespace BoredCareers.Controllers { [ApiController] [Route("api/account/")] public class AuthenticationController : MistoxControllerBase { EmailService _emailContext; public AuthenticationController(DatabaseService db, EmailService emailContext) : base(db) { _emailContext = emailContext; } [Route("login")] [HttpPost] public async Task> Login([FromForm] string UserName, [FromForm] string PasswordHash, [FromForm] bool StayLoggedIn) { try { Account? test = await _databaseService.GetAccount(UserName.ToLower()); if (test != null) { if (test.EmailVerified == true) { if (test.FailedPasswordLock) { if (test.CurrentPasswordAttempts >= test.PasswordAttempts) { return NotFound("Too many failed password attempts. Please reset your password"); } } if (BCrypt.Net.BCrypt.Verify(PasswordHash, test.PasswordHash)) { test.CurrentPasswordAttempts = 0; await _databaseService.SetAccount(test); string jwt = BoredCareersJWT.GenereateJWTToken(test.ID, StayLoggedIn); BoredCareersJWT.SignIn(Response, StayLoggedIn, jwt); return Ok(test); } else { test.CurrentPasswordAttempts += 1; await _databaseService.SetAccount(test); return NotFound("Wrong Password"); } } else { await SendVerify(test.UserName); return NotFound("A new verify email has been sent. \n Note only 1 email send every 5 mintes"); } } return NotFound("Account Not Found"); } catch (Exception ex) { Console.WriteLine("Login Error: " + ex.Message); return NotFound("An internal server error has occured"); } } [Route("register")] [HttpPost] public async Task> Register([FromForm] string Email, [FromForm] string UserName, [FromForm] string PasswordHash) { try { if (await _databaseService.GetAccount(UserName.ToLower()) == null) { if (await _databaseService.GetAccount(Email.ToLower()) == null) { Account created = new Account() { UserName = UserName.ToLower(), Email = Email.ToLower(), EmailVerified = false, PasswordHash = BCrypt.Net.BCrypt.HashPassword(PasswordHash), }; await _databaseService.SetAccount(created); Account? loadedAccount = await _databaseService.GetAccount(Email.ToLower()); if (loadedAccount != null) { await SendVerify(loadedAccount.UserName); return Ok(loadedAccount); } return NotFound("Unable to create the account"); } else { return NotFound("Email is already in use"); } } else { return NotFound("UserName is taken"); } } catch (Exception ex) { Console.WriteLine("Register Error: " + ex.Message); return NotFound("An internal server error has occured"); } } [Route("changepassword")] [HttpPost] public async Task ChangePassword([FromForm] string OldPassword, [FromForm] string NewPassword) { try { if (isLoggedIn()) { Account user = await getLoggedInUser(); if (BCrypt.Net.BCrypt.Verify(OldPassword, user.PasswordHash)) { user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(NewPassword); user.CurrentPasswordAttempts = 0; await _databaseService.SetAccount(user); return Ok(); } } return NotFound("Not logged in"); } catch (Exception ex) { Console.WriteLine("ChangePassword Error: " + ex.Message); return NotFound("An internal server error has occured"); } } [Route("toggleaccountlock")] [HttpPost] public async Task> ToggleAccountLock([FromForm] bool AccountLock) { try { if (isLoggedIn()) { Account user = await getLoggedInUser(); user.FailedPasswordLock = AccountLock; user.CurrentPasswordAttempts = 0; await _databaseService.SetAccount(user); return Ok(); } return NotFound("Not logged in"); } catch (Exception ex) { Console.WriteLine("ToggleAccountLock Error: " + ex.Message); return NotFound("An internal server error has occured"); } } [Route("get")] [HttpPost] public async Task> Get() { try { if (isLoggedIn()) { return Ok(await getLoggedInUser()); } return NotFound("Not logged in"); } catch (Exception ex) { Console.WriteLine("Get Error: " + ex); return NotFound("An internal server error has occured"); } } [Route("logout")] [HttpPost] public ActionResult Logout() { if (isLoggedIn()) { BoredCareersJWT.SignOut(Response); return Ok(); } return NotFound(); } [Route("sendverifyemail")] [HttpPost] public async Task> SendVerify([FromForm] string UserName) { try { string key = "v" + UserName; // Stop from sending multiple emails quickly if (_emailContext._SentEmails.ContainsKey(key)) { DateTime PreviousSentTime = _emailContext._SentEmails.GetValueOrDefault(key); if (PreviousSentTime.AddMinutes(5) > DateTime.Now) { return NotFound("Cannot sent another verify email until 5 minutes has elapsed"); } else { _emailContext._SentEmails.Remove(key); } } Account? test = await _databaseService.GetAccount(UserName.ToLower()); if (test != null) { test.EmailToken = Guid.NewGuid().ToString(); await _databaseService.SetAccount(test); string EmailContents = EmailService.VerifyEmailEmail; EmailContents = Substitue(EmailContents, "@UserName", UserName); EmailContents = Substitue(EmailContents, "@UserName", UserName); EmailContents = Substitue(EmailContents, "@VerifyPassword", test.EmailToken); string result = _emailContext.Send(test.Email, EmailService.VerifyEmailSubject, EmailContents); _emailContext._SentEmails.Add(key, DateTime.Now); return Ok(result); } return NotFound("Account not found"); } catch (Exception) { return NotFound("An internal server error has occured"); } } [Route("verifyemail")] [HttpPost] public async Task> VerifyEmail([FromForm] string UserName, [FromForm] string EmailToken) { try { Account? test = await _databaseService.GetAccount(UserName.ToLower()); if (test != null) { if (!string.IsNullOrEmpty(test.EmailToken) && test.EmailToken == EmailToken) { test.EmailToken = ""; test.EmailVerified = true; await _databaseService.SetAccount(test); return Ok(true); } } return NotFound("Account not found or token is invalid");; } catch { return NotFound("An internal server error has occured"); } } [Route("sendresetpassword")] [HttpPost] public async Task> ResetPassword([FromForm] string Email) { try { string key = "p" + Email.ToLower(); // Stop from sending multiple emails quickly if (_emailContext._SentEmails.ContainsKey(key)) { DateTime PreviousSentTime = _emailContext._SentEmails.GetValueOrDefault(key); if (PreviousSentTime.AddMinutes(5) > DateTime.Now) { return NotFound("Cannot sent another reset requests until 5 minutes has elapsed"); } else { _emailContext._SentEmails.Remove(key); } } Account? test = await _databaseService.GetAccount(Email.ToLower()); if (test != null) { test.EmailToken = Guid.NewGuid().ToString(); await _databaseService.SetAccount(test); string EmailContents = EmailService.ResetPasswordEmail; EmailContents = Substitue(EmailContents, "@UserName", test.UserName); EmailContents = Substitue(EmailContents, "@UserName", test.UserName); EmailContents = Substitue(EmailContents, "@ResetPassWord", test.EmailToken); string result = _emailContext.Send(test.Email, EmailService.VerifyEmailSubject, EmailContents); _emailContext._SentEmails.Add(key, DateTime.Now); return Ok(result); } return NotFound("Account Not Found"); } catch (Exception e) { Console.WriteLine("EmailService Error: " + e.ToString()); return NotFound("An internal server error has occured"); } } [Route("resetpassword")] [HttpPost] public async Task> ResetPwdVerify([FromForm] string UserName, [FromForm] string NewPassword, [FromForm] string ResetToken) { try { Account? test = await _databaseService.GetAccount(UserName.ToLower()); if (test != null && !string.IsNullOrEmpty(test.EmailToken)) { if (!string.IsNullOrEmpty(test.EmailToken) && test.EmailToken == ResetToken) { test.CurrentPasswordAttempts = 0; test.EmailToken = ""; test.PasswordHash = BCrypt.Net.BCrypt.HashPassword(NewPassword); await _databaseService.SetAccount(test); return Ok(true); } } return NotFound("Account not found or reset token is bad"); } catch { return NotFound("An internal server error has occured"); } } [Route("delete")] [HttpPost] public async Task delete([FromForm] string Password) { try { if (isLoggedIn()) { Account user = await getLoggedInUser(); if (BCrypt.Net.BCrypt.Verify(Password, user.PasswordHash)) { await _databaseService.DeleteAccount(user.ID); return Ok(); } } return NotFound("User is not logged in"); } catch (Exception ex) { Console.WriteLine("Delete Error: " + ex.Message); return NotFound("An internal server error has occured"); } } } }