From fe3401a9b35c4fa3c77c7ec31d7522a744cc095c Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Sat, 26 Jul 2025 08:59:42 -0700 Subject: [PATCH] V1 MAuth --- src/Server/Controllers/MAuth.cs | 101 ++++++++++++++++++++++++++++ src/Server/DTO/AuthenticationDTO.cs | 6 +- src/Server/Entities/JWTMemCache.cs | 6 ++ 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 src/Server/Controllers/MAuth.cs create mode 100644 src/Server/Entities/JWTMemCache.cs diff --git a/src/Server/Controllers/MAuth.cs b/src/Server/Controllers/MAuth.cs new file mode 100644 index 0000000..e943bb8 --- /dev/null +++ b/src/Server/Controllers/MAuth.cs @@ -0,0 +1,101 @@ +using Microsoft.AspNetCore.Mvc; +using Auth.Services.DatabaseService; +using System.Web.Http; +using Auth.Entities; +using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using Auth.Services; +using Auth.DTO; + +namespace Auth.Controllers { + [ApiController] + [Route("api/v1/mauth/")] + public class MAuthController : MistoxControllerBase { + + public MAuthController(DatabaseService db) : base(db) { } + + static Dictionary LoginSessions = new Dictionary(); + + // Login and return a ticket to retreive your JWT + [HttpPost("login")] + 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); + + string Ticket = Guid.NewGuid().ToString().Replace("-", ""); + LoginSessions[Ticket] = new JWTMemCache { + JWT = AuthJWT.GenereateJWTToken(test, request.StayLoggedIn), + ExpiresAt = DateTime.UtcNow.AddMinutes(2) + }; + + return Ok(Ticket); + } else { + test.CurrentPasswordAttempts += 1; + await _databaseService.SetAccount(test); + return BadRequest("Wrong Password"); + } + } else { + return BadRequest("You need to verify your email before you can sign in"); + } + } + return BadRequest("Account Not Found"); + } catch (Exception ex) { + Console.WriteLine("Login Error: " + ex.Message); + return BadRequest("An internal server error has occured"); + } + } + + [HttpPost("token")] + public ActionResult Token([FromForm] JWTRequest request) { + try { + if (LoginSessions.ContainsKey(request.Ticket)) { + JWTMemCache JWTObj = LoginSessions[request.Ticket]; + if (JWTObj.ExpiresAt < DateTime.UtcNow) { + string JWT = JWTObj.JWT; + LoginSessions.Remove(request.Ticket); + return Ok(JWT); + } else { + LoginSessions.Remove(request.Ticket); + return BadRequest("The session ticket has already expired"); + } + } + return BadRequest("The session ticket cannot be found"); + } catch (Exception ex) { + Console.WriteLine("Delete Error: " + ex.Message); + return BadRequest("An internal server error has occured"); + } + } + + // Renews an old JWT before it expires + [HttpPost("renew")] + public IActionResult Session([FromBody] JWTRenewRequest request) { + try { + JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler(); + ClaimsPrincipal claimsPrincipal = handler.ValidateToken(request.JWT, 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 BadRequest("Token invalid: " + ex.Message); + } + } + + } +} diff --git a/src/Server/DTO/AuthenticationDTO.cs b/src/Server/DTO/AuthenticationDTO.cs index c2eddcb..416afc1 100644 --- a/src/Server/DTO/AuthenticationDTO.cs +++ b/src/Server/DTO/AuthenticationDTO.cs @@ -7,7 +7,7 @@ namespace Auth.DTO { } public class JWTRenewRequest { - public string Token { get; set; } = ""; + public string JWT { get; set; } = ""; } public class RegisterRequest { @@ -48,4 +48,8 @@ namespace Auth.DTO { public string Password { get; set; } = ""; } + public class JWTRequest { + public string Ticket { get; set; } = ""; + } + } \ No newline at end of file diff --git a/src/Server/Entities/JWTMemCache.cs b/src/Server/Entities/JWTMemCache.cs new file mode 100644 index 0000000..1cba505 --- /dev/null +++ b/src/Server/Entities/JWTMemCache.cs @@ -0,0 +1,6 @@ +namespace Auth.Entities { + public class JWTMemCache { + public string JWT { get; set; } = ""; + public DateTime ExpiresAt { get; set; } + } +} \ No newline at end of file