using CPRNIMS.Domain.Contracts.Account; using CPRNIMS.Domain.Services; using CPRNIMS.Infrastructure.Entities.Account; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using CPRNIMS.Infrastructure.Models.Common; using CPRNIMS.Infrastructure.Helper; using CPRNIMS.Infrastructure.Entities.Common; using CPRNIMS.Infrastructure.ViewModel.Common; using CPRNIMS.Infrastructure.Dto.Account; using System.Threading.Tasks; namespace CPRNIMS.WebApi.Controllers.Account { public class AnonController : Base.BaseController { private readonly SMTPHelper _smtpHelper; private readonly IForgotPassword _forgotPassword; private readonly UserManager _userManager; private readonly SignInManager _signInManager; private readonly IConfiguration _config; public AnonController(ErrorMessageService errorMessageService, IWebHostEnvironment webHostEnvironment, SMTPHelper sMTPHelper, IConfiguration configuration, IForgotPassword forgotPassword, IDepartment department , SignInManager signInManager, UserManager userManager ) : base(errorMessageService, webHostEnvironment, configuration) { _config = configuration; _smtpHelper = sMTPHelper; _forgotPassword = forgotPassword; _userManager = userManager; _signInManager = signInManager; } [AllowAnonymous] [HttpPost("Login")] public async Task Login([FromBody] LoginRequest model, [FromServices] IAccount tokenService) { try { var user = await _userManager.FindByNameAsync(model.UserName.ToLower()); if (user == null) return BadRequest(new ResponseObject { success = false, messCode = 0, message = "Invalid username or password." }); var signInResult = await _signInManager.CheckPasswordSignInAsync(user, model.Password, false); if (signInResult.Succeeded) { await HandleSuccessfulLogin(user); var token = await tokenService.CreateToken(user); return Ok(new { token, expiresAt= DateTime.UtcNow.AddHours(2), userId = user.Id, userName = user.UserName, fullName = user.FullName, departmentId = user.DepartmentId, email = user.Email, phoneNumber = user.PhoneNumber, company = user.Company, success = true, messCode = 1, message = "Success" }); } return await HandleFailedLogin(user, signInResult); } catch (Exception ex) { var message = ex.InnerException?.Message ?? ex.Message; return BadRequest(new ResponseObject { success = false, messCode = 0, message = message }); } } protected async Task HandleSuccessfulLogin(ApplicationUser user) { // Unlock if necessary if (user.LockoutEnabled || user.LockoutEnd != null) { await _userManager.SetLockoutEnabledAsync(user, false); user.LockoutEnd = null; await _userManager.UpdateAsync(user); } // Reset failed attempts await _userManager.ResetAccessFailedCountAsync(user); } protected async Task HandleFailedLogin(ApplicationUser user, Microsoft.AspNetCore.Identity.SignInResult signInResult) { // Increment failed attempts await _userManager.AccessFailedAsync(user); if (user.AccessFailedCount > 3 || signInResult.IsLockedOut) { await _userManager.SetLockoutEnabledAsync(user, true); await _userManager.SetLockoutEndDateAsync(user, DateTime.Now.AddMinutes(30)); return BadRequest(new ResponseObject { success = false, messCode = 0, message = "Account is locked. Please try again after 30 minutes or contact support." }); } return BadRequest(new ResponseObject { success = false, messCode = 0, message = "Invalid username or password, please double check!" }); } [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; } } } }