using Microsoft.AspNetCore.Mvc; using Auth.Services; using Auth.Services.DatabaseService; using Auth.Entities; using Auth.DTO; using System.Web.Http; using System.IdentityModel.Tokens.Jwt; using Microsoft.IdentityModel.Tokens; using System.Text; using System.Security.Claims; namespace Auth.Controllers { [ApiController] [Route("api/")] public class AuthenticationController : MistoxControllerBase { EmailService _emailContext; public AuthenticationController(DatabaseService db, EmailService emailContext) : base(db) { _emailContext = emailContext; } [Route("get")] [HttpPost] public async Task> Get() { try { if (isLoggedIn()) { return Ok(await getLoggedInUser()); } return BadRequest("Not logged in"); } catch (Exception ex) { Console.WriteLine("Get Error: " + ex); return BadRequest("An internal server error has occured"); } } [Route("login")] [HttpPost] public async Task> Login([FromBody] LoginRequest request) { try { Account? test = await _databaseService.GetAccount(request.UserName.ToLower()); if (test != null) { if (test.EmailVerified == true) { if (test.FailedPasswordLock) { if (test.CurrentPasswordAttempts >= test.PasswordAttempts) { return BadRequest("Too many failed password attempts. Please reset your password"); } } if (BCrypt.Net.BCrypt.Verify(request.Password, test.PasswordHash)) { test.CurrentPasswordAttempts = 0; await _databaseService.SetAccount(test); string jwt = AuthJWT.GenereateJWTToken(test, request.StayLoggedIn); AuthJWT.SignIn(Response, request.StayLoggedIn, jwt); return Ok(test); } else { test.CurrentPasswordAttempts += 1; await _databaseService.SetAccount(test); return BadRequest("Wrong Password"); } } else { await SendVerify(new SendVerifyEmailRequest { UserName = test.UserName }); return BadRequest("A new verify email has been sent. \n Note only 1 email send every 5 mintes"); } } return BadRequest("Account Not Found"); } catch (Exception ex) { Console.WriteLine("Login Error: " + ex.Message); return BadRequest("An internal server error has occured"); } } [Route("jwtlogin")] [HttpPost] public async Task Authenticate([FromBody] LoginRequest request) { try { Account? test = await _databaseService.GetAccount(request.UserName.ToLower()); if (test != null) { if (test.EmailVerified == true) { if (test.FailedPasswordLock) { if (test.CurrentPasswordAttempts >= test.PasswordAttempts) { return BadRequest("Too many failed password attempts. Please reset your password"); } } if (BCrypt.Net.BCrypt.Verify(request.Password, test.PasswordHash)) { test.CurrentPasswordAttempts = 0; await _databaseService.SetAccount(test); return Ok(AuthJWT.GenereateJWTToken(test, request.StayLoggedIn)); } else { test.CurrentPasswordAttempts += 1; await _databaseService.SetAccount(test); return BadRequest("Wrong Password"); } } else { await SendVerify(new SendVerifyEmailRequest { UserName = test.UserName }); return BadRequest("A new verify email has been sent. \n Note only 1 email send every 5 mintes"); } } return BadRequest("Account Not Found"); } catch (Exception ex) { Console.WriteLine("Login Error: " + ex.Message); return BadRequest("An internal server error has occured"); } } [Route("jwttryrenew")] [HttpPost] public IActionResult Refresh([FromBody] JWTRenewRequest request){ try { JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler(); ClaimsPrincipal claimsPrincipal = handler.ValidateToken(request.Token, AuthJWT.TokenParameters, out var validatedToken); JwtSecurityToken jwt = (JwtSecurityToken)validatedToken; if (jwt != null) { if (jwt.ValidTo - DateTime.UtcNow < TimeSpan.FromDays(1)) { var newJwt = AuthJWT.RenewJWTToken(claimsPrincipal); return Ok(newJwt); } return BadRequest("Not ready to renew"); } return BadRequest("Malformed Token"); } catch (SecurityTokenException ex) { return Unauthorized($"Token invalid: {ex.Message}"); } } [Route("logout")] [HttpPost] public ActionResult Logout() { if (isLoggedIn()) { AuthJWT.SignOut(Response); return Ok(); } return BadRequest(); } [Route("register")] [HttpPost] public async Task> Register([FromBody] RegisterRequest request) { try { if (await _databaseService.GetAccount(request.UserName.ToLower()) == null) { if (await _databaseService.GetAccount(request.Email.ToLower()) == null) { Account created = new Account() { UserName = request.UserName.ToLower(), Email = request.Email.ToLower(), EmailVerified = false, PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.Password), }; await _databaseService.SetAccount(created); Account? loadedAccount = await _databaseService.GetAccount(request.Email.ToLower()); if (loadedAccount != null) { await SendVerify(new SendVerifyEmailRequest { UserName = loadedAccount.UserName }); return Ok(loadedAccount); } return BadRequest("Unable to create the account"); } else { return BadRequest("Email is already in use"); } } else { return BadRequest("UserName is taken"); } } catch (Exception ex) { Console.WriteLine("Register Error: " + ex.Message); return BadRequest("An internal server error has occured"); } } [Route("changepassword")] [HttpPost] public async Task ChangePassword([FromBody] ChangePasswordRequest request) { try { if (isLoggedIn()) { Account user = await getLoggedInUser(); if (BCrypt.Net.BCrypt.Verify(request.OldPassword, user.PasswordHash)) { user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.NewPassword); user.CurrentPasswordAttempts = 0; await _databaseService.SetAccount(user); return Ok(); } } return BadRequest("Not logged in"); } catch (Exception ex) { Console.WriteLine("ChangePassword Error: " + ex.Message); return BadRequest("An internal server error has occured"); } } [Route("accountlock")] [HttpPost] public async Task> setAccountLock([FromBody] AccountLockRequest request) { try { if (isLoggedIn()) { Account user = await getLoggedInUser(); user.FailedPasswordLock = request.AccountLock; user.CurrentPasswordAttempts = 0; await _databaseService.SetAccount(user); return Ok(); } return BadRequest("Not logged in"); } catch (Exception ex) { Console.WriteLine("ToggleAccountLock Error: " + ex.Message); return BadRequest("An internal server error has occured"); } } [Route("sendverifyemail")] [HttpPost] public async Task> SendVerify([FromBody] SendVerifyEmailRequest request) { try { string key = "v" + request.UserName.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 BadRequest("Cannot sent another verify email until 5 minutes has elapsed"); } else { _emailContext._SentEmails.Remove(key); } } Account? test = await _databaseService.GetAccount(request.UserName.ToLower()); if (test != null) { test.EmailToken = Guid.NewGuid().ToString(); test.EmailTokenCreated = DateTime.UtcNow; await _databaseService.SetAccount(test); string EmailContents = EmailService.VerifyEmailEmail; EmailContents = Substitue(EmailContents, "@UserName", request.UserName); EmailContents = Substitue(EmailContents, "@UserName", request.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 BadRequest("Account not found"); } catch (Exception) { return BadRequest("An internal server error has occured"); } } [Route("verifyemail")] [HttpPost] public async Task> VerifyEmail([FromBody] VerifyEmailRequest request) { try { Account? test = await _databaseService.GetAccount(request.UserName.ToLower()); if (test != null) { if (DateTime.UtcNow < test.EmailTokenCreated.AddMinutes(30)) { if (test.EmailToken == request.EmailToken) { test.EmailToken = ""; test.EmailVerified = true; await _databaseService.SetAccount(test); return Ok(true); } return BadRequest("The token isn't valid"); } return BadRequest("Your email token has timed out"); } return BadRequest("Account not found"); ; } catch { return BadRequest("An internal server error has occured"); } } [Route("sendresetpassword")] [HttpPost] public async Task> ResetPassword([FromBody] SendResetPasswordRequest request) { try { string key = "p" + request.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 BadRequest("Cannot sent another reset requests until 5 minutes has elapsed"); } else { _emailContext._SentEmails.Remove(key); } } Account? test = await _databaseService.GetAccount(request.Email.ToLower()); if (test != null) { test.PasswordToken = Guid.NewGuid().ToString(); test.PasswordTokenCreated = DateTime.UtcNow; 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 BadRequest("Account Not Found"); } catch (Exception e) { Console.WriteLine("EmailService Error: " + e.ToString()); return BadRequest("An internal server error has occured"); } } [Route("resetpassword")] [HttpPost] public async Task> ResetPwdVerify([FromBody] ResetPasswordRequest request) { try { Account? test = await _databaseService.GetAccount(request.UserName.ToLower()); if (test != null) { if (DateTime.UtcNow < test.PasswordTokenCreated.AddMinutes(30)) { if (test.PasswordToken == request.PasswordToken) { test.CurrentPasswordAttempts = 0; test.PasswordToken = ""; test.PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.NewPassword); await _databaseService.SetAccount(test); return Ok(true); } return BadRequest("The token isn't valid"); } return BadRequest("Your email token has timed out"); } return BadRequest("Account not found"); ; } catch { return BadRequest("An internal server error has occured"); } } [Route("delete")] [HttpPost] public async Task delete([FromBody] DeleteRequest request) { try { if (isLoggedIn()) { Account user = await getLoggedInUser(); if (BCrypt.Net.BCrypt.Verify(request.Password, user.PasswordHash)) { await _databaseService.DeleteAccount(user.ID); return Ok(); } } return BadRequest("User is not logged in"); } catch (Exception ex) { Console.WriteLine("Delete Error: " + ex.Message); return BadRequest("An internal server error has occured"); } } } }