using CPRNIMS.Domain.Contracts.Account; using CPRNIMS.Domain.Services.Account; using CPRNIMS.Domain.Services; using CPRNIMS.Infrastructure.Entities.Account; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using CPRNIMS.Infrastructure.Models.Common; using CPRNIMS.Infrastructure.Models.Account; using Microsoft.AspNetCore.Hosting; using CPRNIMS.Infrastructure.Helper; using CPRNIMS.Infrastructure.Entities.Common; using CPRNIMS.Infrastructure.ViewModel.Common; namespace CPRNIMS.WebApi.Controllers.Account { public class AnonController : Base.BaseController { private readonly SMTPHelper _smtpHelper; public readonly IForgotPassword _forgotPassword; public readonly UserManager _userManager; public readonly SignInManager _signInManager; public readonly UserClaimsManager _userClaimsManager; public readonly RoleManager _roleManager; public readonly IControllerAccess _controllerAccess; public readonly IDepartment _department; public readonly IConfiguration _config; public readonly IAccount _account; public AnonController(ErrorMessageService errorMessageService, IWebHostEnvironment webHostEnvironment , SMTPHelper sMTPHelper, IForgotPassword forgotPassword , UserManager userManager , SignInManager signInManager , IConfiguration configuration , UserClaimsManager userClaimsManager, RoleManager roleManager , IControllerAccess controllerAccess, IDepartment department , IAccount account) : base(errorMessageService, webHostEnvironment, configuration) { _smtpHelper = sMTPHelper; _forgotPassword = forgotPassword; _userManager = userManager; _signInManager = signInManager; _userClaimsManager = userClaimsManager; _roleManager = roleManager; _controllerAccess = controllerAccess; _department = department; _config = configuration; _account = account; } [AllowAnonymous] [HttpPost("GetToken")] public async Task GetToken([FromBody] User model) { try { var user = await _userManager.FindByNameAsync(model.UserName.ToLower()); var userRoles = await _userManager.GetRolesAsync(user); var signInResult = await _signInManager.CheckPasswordSignInAsync(user, model.Password, lockoutOnFailure: false); if (signInResult.Succeeded) { var authClaims = new List { new Claim(ClaimTypes.Name, user.UserName), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; foreach (var userRole in userRoles) { authClaims.Add(new Claim(ClaimTypes.Role, userRole)); } var token = GetToken(authClaims); return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token), expiration = token.ValidTo }); } return BadRequest(); } catch (Exception ex) { var message = ex.InnerException?.ToString() ?? ex.Message.ToString(); await PostErrorMessage(message, " WebApi"); throw; } } [AllowAnonymous] [HttpPost("login")] public async Task Login([FromBody] User model) { try { var user = await _userManager.FindByNameAsync(model.UserName.ToLower()); if (user != null) { var signInResult = await _signInManager.CheckPasswordSignInAsync(user, model.Password, lockoutOnFailure: false); if (signInResult.Succeeded) { if (user.LockoutEnabled == true || user.LockoutEnd != null) { await _userManager.SetLockoutEnabledAsync(user, false); user.LockoutEnd = null; await _userManager.UpdateAsync(user); } // Reset access failed count upon successful login await _userManager.ResetAccessFailedCountAsync(user); var userRoles = await _userManager.GetRolesAsync(user); try { var authClaims = new List { new Claim(ClaimTypes.Name, user.UserName), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; foreach (var userRole in userRoles) { authClaims.Add(new Claim(ClaimTypes.Role, userRole)); } var token = GetToken(authClaims); return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token), expiration = token.ValidTo }); } catch (Exception ex) { var message = ex.InnerException?.ToString() ?? ex.Message.ToString(); await PostErrorMessage(message, " WebApi"); throw; } } else { // Increment access failed count await _userManager.AccessFailedAsync(user); // Check if the access failed count reaches a threshold if (user.AccessFailedCount > 3) { await _userManager.SetLockoutEnabledAsync(user, true); await _userManager.SetLockoutEndDateAsync(user, DateTime.Now.AddMinutes(30)); // Lock the account for 30 minutes (you can adjust as needed) return BadRequest(new ResponseObject { success = false, statusResponse = "Failed", message = "Account is locked. Please try again after 30 minutes or contact support." }); } else if (signInResult.IsLockedOut) { // Increment access failed count await _userManager.AccessFailedAsync(user); return BadRequest(new ResponseObject { success = false,statusResponse = "Failed", message = "Account is locked. Please try again after 30 minutes or contact support." }); } //If the else { return BadRequest(new ResponseObject { success = false, statusResponse = "Failed", message = "Invalid UserName or Password, please double check!" }); } } } return BadRequest(new ResponseObject { success = false, statusResponse = "Failed", message = "Invalid UserName or Password, please double check!" }); } catch (Exception ex) { var message = ex.InnerException?.ToString() ?? ex.Message.ToString(); await PostErrorMessage(message, " WebApi"); return BadRequest(new ResponseObject { success = false, statusResponse = "Failed", message = message }); } } [AllowAnonymous] [HttpPost("ValidateOTP")] public async Task ValidateOTP([FromBody] EmailMessageDetailsVM viewModel) { try { var otpModel = new Otps { OTP = viewModel.OTP, Email = viewModel.Email, }; if (String.IsNullOrEmpty(otpModel.Email) || String.IsNullOrEmpty(otpModel.OTP)) { return NotFound(); } var isEmailExist = await _userManager.FindByEmailAsync(otpModel.Email.ToLower()); if (isEmailExist != null) { bool isOTPValid = await _forgotPassword.ValidateOTP(otpModel); if (isOTPValid) { return Ok(new ResponseObject { messCode = 1, message = "You may proceed now." }); } return Ok(new ResponseObject { messCode = 0, message = "Your OTP is invalid!." }); } return NotFound(); } catch (Exception ex) { var message = ex.InnerException?.ToString() ?? ex.Message.ToString(); await PostErrorMessage(message, "ForgotPassWebApi"); throw; } } [AllowAnonymous] [HttpPost("GetUserByEmail")] public async Task GetUserByEmail([FromBody] EmailMessageDetailsVM model) { try { if (string.IsNullOrEmpty(model.Email)) { return NotFound(); } var isEmailExist = await _userManager.FindByEmailAsync(model.Email.ToLower()); if (isEmailExist != null) { var otpGenerated = _smtpHelper.GenerateOTP(); var otpDetails = new Otps { OTP = otpGenerated, Email = model.Email, CreatedBy = model.Email, CreatedDate = DateTime.Now, UpdatedBy = model.Email, UpdatedDate = DateTime.Now, }; bool isOTPExpired = await _forgotPassword.ValidateOTP(otpDetails); if (isOTPExpired) { otpGenerated = _smtpHelper.GenerateOTP(); } otpDetails.OTP = otpGenerated; var message = EMailTemplate("Content/SMTPEmailContent", "ForgotPassword.cshtml"); message = message.Replace("@ViewBag.FullName", isEmailExist.FullName); message = message.Replace("@ViewBag.OTPCode", otpGenerated); var messageDetails = new EmailMessageDetailsVM { Message = message, Subject = "CLMS - OTP code for password reset", SenderEmail = _config["SMTP:SenderEmail"], DisplayName = _config["SMTP:DisplayName"], Recipient = model.Email, Bcc = _config["SMTP:CC"], CC = _config["SMTP:CC"], NewPassword = _config["SMTP:Password"], OutGoingPort = 587, Server = _config["SMTP:Server"], UserName = _config["SMTP:UserName"] }; await _forgotPassword.SaveUpdateOTPAsync(otpDetails, true); await _forgotPassword.SaveUpdateOTPAsync(otpDetails, false); await _smtpHelper.SendEmailAsync(messageDetails); return Ok(new ResponseObject { messCode = 1, message = "You may proceed now." }); } return NotFound(); } catch (Exception ex) { var message = ex.InnerException?.ToString() ?? ex.Message.ToString(); await PostErrorMessage(message, "ForgotPassWebApi"); throw; } } [AllowAnonymous] [HttpPut("ChangePassword")] public async Task ChangePassword([FromBody] EmailMessageDetailsVM model) { try { var isEmailExist = await _userManager.FindByEmailAsync(model?.Email); //If this email is exist then go to 2nd validation if (isEmailExist != null) { var passwordToken = ""; // Check if the Password field is not empty if (!string.IsNullOrEmpty(model.PasswordHash)) { // Change the user's password passwordToken = await _userManager.GeneratePasswordResetTokenAsync(isEmailExist); var passwordChangeResult = await _userManager.ResetPasswordAsync(isEmailExist, passwordToken, model.PasswordHash); if (!passwordChangeResult.Succeeded) { string passwordErrorMessage = passwordChangeResult.Errors?.FirstOrDefault().Description; return StatusCode(StatusCodes.Status500InternalServerError, new ResponseObject { messCode = 0, message = passwordErrorMessage }); } } var otps = new Otps { Email = model.Email, OTP = model.OTP }; bool isOTPValid = await _forgotPassword.ValidateOTP(otps); if (isOTPValid != true) { return NotFound(new ResponseObject { IsValid = false, message = "Your OTP is invalid or expired!." }); } else { isEmailExist.UpdatedDate = DateTime.Now; isEmailExist.UpdatedBy = model.Email; isEmailExist.LockoutEnabled = false; isEmailExist.LockoutEnd = null; isEmailExist.AccessFailedCount = 0; var result = await _userManager.UpdateAsync(isEmailExist); if (!result.Succeeded) { string errorMessage = result.Errors.FirstOrDefault()?.Description; return StatusCode(StatusCodes.Status500InternalServerError, new ResponseObject { messCode = 0, message = errorMessage }); } await _forgotPassword.SaveUpdateOTPAsync(otps, true); return Ok(new ResponseObject { messCode = 1, message = "Your password has been reset successfully!, please check your email." }); } } return NotFound(new ResponseObject { messCode = 0, message = "Email not exist!" }); } catch (Exception ex) { var message = ex.InnerException?.ToString() ?? ex.Message.ToString(); await PostErrorMessage(message, "ForgotPassWebApi"); throw; } } } }