Merge pull request 'Implement API to log in through MPIN authentication.' (#87) from Ashutosh_Task#484_Login_MPIN into Issue_Jun_1W_2
Reviewed-on: #87
This commit is contained in:
commit
9f37c37e18
9
Marco.Pms.Model/Dtos/Authentication/VerifyMPINDto.cs
Normal file
9
Marco.Pms.Model/Dtos/Authentication/VerifyMPINDto.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Marco.Pms.Model.Dtos.Authentication
|
||||
{
|
||||
public class VerifyMPINDto
|
||||
{
|
||||
public Guid EmployeeId { get; set; }
|
||||
public string? MPIN { get; set; }
|
||||
public string? MPINToken { get; set; }
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Net;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
@ -48,34 +49,70 @@ namespace MarcoBMS.Services.Controllers
|
||||
[HttpPost("login")]
|
||||
public async Task<IActionResult> Login([FromBody] LoginDto loginDto)
|
||||
{
|
||||
var user = await _context.ApplicationUsers.FirstOrDefaultAsync(u => u.Email == loginDto.Username || u.PhoneNumber == loginDto.Username);
|
||||
|
||||
if (user != null)
|
||||
try
|
||||
{
|
||||
// Find user by email or phone number
|
||||
var user = await _context.ApplicationUsers
|
||||
.FirstOrDefaultAsync(u => u.Email == loginDto.Username || u.PhoneNumber == loginDto.Username);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogWarning("Login failed: User not found for input {Username}", loginDto.Username ?? string.Empty);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid username or password.", "Invalid username or password.", 401));
|
||||
}
|
||||
|
||||
// Check if the user is active
|
||||
if (!user.IsActive)
|
||||
{
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("User is In Active", "User is In Active", 400));
|
||||
_logger.LogWarning("Login failed: Inactive user attempted login - UserId: {UserId}", user.Id);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("User is inactive", "User is inactive", 400));
|
||||
}
|
||||
|
||||
// Ensure the user's email is confirmed
|
||||
if (!user.EmailConfirmed)
|
||||
{
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Your email is not verified, Please verify your email", "Your email is not verified, Please verify your email", 400));
|
||||
_logger.LogWarning("Login failed: Email not confirmed for UserId: {UserId}", user.Id);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Email not verified", "Your email is not verified, please verify your email", 400));
|
||||
}
|
||||
if (await _userManager.CheckPasswordAsync(user, loginDto.Password ?? string.Empty))
|
||||
|
||||
// Validate the password
|
||||
if (!await _userManager.CheckPasswordAsync(user, loginDto.Password ?? string.Empty))
|
||||
{
|
||||
Employee emp = await _employeeHelper.GetEmployeeByApplicationUserID(user.Id);
|
||||
//var refreshToken = GenerateRefreshToken();
|
||||
if (user.UserName == null) return NotFound(ApiResponse<object>.ErrorResponse("UserName Not found", "UserName Not found", 404)); ;
|
||||
|
||||
var token = _refreshTokenService.GenerateJwtToken(user.UserName, emp.TenantId, _jwtSettings);
|
||||
|
||||
var refreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, emp.TenantId.ToString(), _jwtSettings);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(new { token = token, refreshToken = refreshToken }, "User logged in successfully.", 200));
|
||||
_logger.LogWarning("Login failed: Incorrect password for UserId: {UserId}", user.Id);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid username or password.", "Invalid username or password.", 401));
|
||||
}
|
||||
|
||||
}
|
||||
// Retrieve employee details
|
||||
var emp = await _employeeHelper.GetEmployeeByApplicationUserID(user.Id);
|
||||
if (emp == null)
|
||||
{
|
||||
_logger.LogWarning("Login failed: No employee record found for UserId: {UserId}", user.Id);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Employee record not found", "Employee not found", 404));
|
||||
}
|
||||
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid username or password.", "Invalid username or password.", 401));
|
||||
// Ensure UserName exists for JWT
|
||||
if (string.IsNullOrWhiteSpace(user.UserName))
|
||||
{
|
||||
_logger.LogWarning("Login failed: Username not found for UserId: {UserId}", user.Id);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Username not found", "Username not found", 404));
|
||||
}
|
||||
|
||||
// Generate tokens
|
||||
var token = _refreshTokenService.GenerateJwtToken(user.UserName, emp.TenantId, _jwtSettings);
|
||||
var refreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, emp.TenantId.ToString(), _jwtSettings);
|
||||
|
||||
_logger.LogInfo("User login successful - UserId: {UserId}", user.Id);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(new
|
||||
{
|
||||
token,
|
||||
refreshToken
|
||||
}, "User logged in successfully.", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Unexpected error during login : {Error}", ex.Message);
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error", ex.Message, 500));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("login-mobile")]
|
||||
@ -150,111 +187,272 @@ namespace MarcoBMS.Services.Controllers
|
||||
return Ok(ApiResponse<object>.SuccessResponse(responseData, "User logged in successfully.", 200));
|
||||
}
|
||||
|
||||
[HttpPost("login-mpin")]
|
||||
public async Task<IActionResult> VerifyMPIN([FromBody] VerifyMPINDto verifyMPIN)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Validate the MPIN token and extract claims
|
||||
var claimsPrincipal = _refreshTokenService.ValidateToken(verifyMPIN.MPINToken, _jwtSettings);
|
||||
if (claimsPrincipal?.Identity == null || !claimsPrincipal.Identity.IsAuthenticated)
|
||||
{
|
||||
_logger.LogWarning("Invalid or unauthenticated MPIN token");
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid MPIN token", "Unauthorized", 401));
|
||||
}
|
||||
|
||||
string? tokenType = claimsPrincipal.FindFirst("token_type")?.Value;
|
||||
string? tokenTenantId = claimsPrincipal.FindFirst("TenantId")?.Value;
|
||||
string? tokenUserId = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
|
||||
// Validate essential claims
|
||||
if (string.IsNullOrWhiteSpace(tokenType) || string.IsNullOrWhiteSpace(tokenTenantId) || string.IsNullOrWhiteSpace(tokenUserId))
|
||||
{
|
||||
_logger.LogWarning("MPIN token claims are incomplete");
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid token claims", "MPIN token does not match your identity", 401));
|
||||
}
|
||||
|
||||
Guid tenantId = Guid.Parse(tokenTenantId);
|
||||
|
||||
// Fetch employee by ID and tenant
|
||||
var requestEmployee = await _context.Employees
|
||||
.Include(e => e.ApplicationUser)
|
||||
.FirstOrDefaultAsync(e => e.Id == verifyMPIN.EmployeeId && e.TenantId == tenantId && e.ApplicationUserId == tokenUserId && e.IsActive);
|
||||
|
||||
if (requestEmployee == null || string.IsNullOrWhiteSpace(requestEmployee.ApplicationUserId))
|
||||
{
|
||||
_logger.LogWarning("Employee not found or invalid for verification - EmployeeId: {EmployeeId}", verifyMPIN.EmployeeId);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid request", "Provided invalid employee information", 400));
|
||||
}
|
||||
|
||||
// Validate that the token belongs to the same employee making the request
|
||||
if (requestEmployee.ApplicationUserId != tokenUserId || tokenType != "mpin")
|
||||
{
|
||||
_logger.LogWarning("Token identity does not match employee info - EmployeeId: {EmployeeId}", requestEmployee.Id);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Unauthorized", "MPIN token does not match your identity", 401));
|
||||
}
|
||||
|
||||
// Ensure MPIN input is valid
|
||||
if (string.IsNullOrWhiteSpace(verifyMPIN.MPIN))
|
||||
{
|
||||
_logger.LogWarning("MPIN not provided for EmployeeId: {EmployeeId}", requestEmployee.Id);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid request", "MPIN not provided", 400));
|
||||
}
|
||||
|
||||
// Retrieve MPIN details
|
||||
var mpinDetails = await _context.MPINDetails
|
||||
.FirstOrDefaultAsync(p => p.UserId == Guid.Parse(requestEmployee.ApplicationUserId) && p.TenantId == tenantId);
|
||||
|
||||
if (mpinDetails == null)
|
||||
{
|
||||
_logger.LogWarning("MPIN not set for EmployeeId: {EmployeeId}", requestEmployee.Id);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("MPIN not set", "You have not set an MPIN", 400));
|
||||
}
|
||||
|
||||
// Compare hashed MPIN
|
||||
var providedMPINHash = ComputeSha256Hash(verifyMPIN.MPIN);
|
||||
if (providedMPINHash != mpinDetails.MPIN)
|
||||
{
|
||||
_logger.LogWarning("MPIN mismatch for EmployeeId: {EmployeeId}", requestEmployee.Id);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("MPIN mismatch", "MPIN did not match", 401));
|
||||
}
|
||||
|
||||
// Generate new tokens
|
||||
var jwtToken = _refreshTokenService.GenerateJwtToken(requestEmployee.Email, tenantId, _jwtSettings);
|
||||
var refreshToken = await _refreshTokenService.CreateRefreshToken(requestEmployee.ApplicationUserId, tenantId.ToString(), _jwtSettings);
|
||||
|
||||
_logger.LogInfo("MPIN verification successful - EmployeeId: {EmployeeId}", requestEmployee.Id);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(new
|
||||
{
|
||||
token = jwtToken,
|
||||
refreshToken
|
||||
}, "User logged in successfully.", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Unexpected error occurred while verifying MPIN : {Error}", ex.Message);
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error", ex.Message, 500));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("logout")]
|
||||
public async Task<IActionResult> Logout([FromBody] LogoutDto logoutDto)
|
||||
{
|
||||
if (string.IsNullOrEmpty(logoutDto.RefreshToken))
|
||||
if (string.IsNullOrWhiteSpace(logoutDto.RefreshToken))
|
||||
{
|
||||
_logger.LogWarning("Logout failed: Refresh token is missing");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Refresh token is required", "Refresh token is required", 400));
|
||||
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Revoke the refresh token
|
||||
bool isRevoked = await _refreshTokenService.RevokeRefreshTokenAsync(logoutDto.RefreshToken);
|
||||
|
||||
if (!isRevoked)
|
||||
{
|
||||
_logger.LogWarning("Logout failed: Invalid or expired refresh token");
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid or expired refresh token", "Invalid or expired refresh token", 401));
|
||||
}
|
||||
|
||||
|
||||
// Optional: Blacklist the access token (JWT)
|
||||
// Optional: Blacklist the JWT access token
|
||||
string jwtToken = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
|
||||
if (!string.IsNullOrEmpty(jwtToken))
|
||||
if (!string.IsNullOrWhiteSpace(jwtToken))
|
||||
{
|
||||
await _refreshTokenService.BlacklistJwtTokenAsync(jwtToken);
|
||||
_logger.LogInfo("JWT access token blacklisted successfully");
|
||||
}
|
||||
|
||||
_logger.LogInfo("User logged out successfully");
|
||||
return Ok(ApiResponse<object>.SuccessResponse(new { }, "Logged out successfully", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// _logger.LogError(ex, "Error during logout");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Internal server error", ex.Message, 500));
|
||||
_logger.LogError("Unexpected error during logout : {Error}", ex.Message);
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error occurred", ex.Message, 500));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("refresh-token")]
|
||||
public async Task<IActionResult> RefreshToken([FromBody] RefreshTokenDto refreshTokenDto)
|
||||
{
|
||||
var refreshToken = await _refreshTokenService.GetRefreshToken(refreshTokenDto.RefreshToken);
|
||||
if (refreshToken == null || refreshToken.ExpiryDate < DateTime.UtcNow)
|
||||
if (string.IsNullOrWhiteSpace(refreshTokenDto.RefreshToken))
|
||||
{
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid or expired refresh token.", "Invalid or expired refresh token.", 401));
|
||||
_logger.LogWarning("Refresh token is missing from the request body.");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Refresh token is required.", "Missing refresh token.", 400));
|
||||
}
|
||||
|
||||
// Mark token as used
|
||||
await _refreshTokenService.MarkRefreshTokenAsUsed(refreshToken);
|
||||
try
|
||||
{
|
||||
// Step 1: Fetch and validate the refresh token
|
||||
var refreshToken = await _refreshTokenService.GetRefreshToken(refreshTokenDto.RefreshToken);
|
||||
if (refreshToken == null)
|
||||
{
|
||||
_logger.LogWarning("Refresh token not found in the database");
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid or expired refresh token.", "Token not found.", 401));
|
||||
}
|
||||
|
||||
// Generate new JWT token and refresh token
|
||||
var user = await _userManager.FindByIdAsync(refreshToken.UserId ?? string.Empty);
|
||||
if (user == null)
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid request.", "Invalid request.", 400));
|
||||
if (refreshToken.ExpiryDate < DateTime.UtcNow)
|
||||
{
|
||||
_logger.LogWarning("Refresh token expired");
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Refresh token expired.", "Token expired.", 401));
|
||||
}
|
||||
|
||||
Employee emp = await _employeeHelper.GetEmployeeByApplicationUserID(user.Id);
|
||||
// Step 2: Mark the token as used
|
||||
await _refreshTokenService.MarkRefreshTokenAsUsed(refreshToken);
|
||||
_logger.LogInfo("Refresh token marked as used");
|
||||
|
||||
if (user.UserName == null) return NotFound(ApiResponse<object>.ErrorResponse("UserName Not found", "UserName Not found", 404));
|
||||
// Step 3: Validate and retrieve user
|
||||
var user = await _userManager.FindByIdAsync(refreshToken.UserId ?? string.Empty);
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogWarning("User not found for RefreshToken: {Token}", refreshTokenDto.RefreshToken);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid request.", "User not found.", 400));
|
||||
}
|
||||
|
||||
var newJwtToken = _refreshTokenService.GenerateJwtToken(user.UserName, emp.TenantId, _jwtSettings);
|
||||
var newRefreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, emp.TenantId.ToString(), _jwtSettings);
|
||||
if (string.IsNullOrWhiteSpace(user.UserName))
|
||||
{
|
||||
_logger.LogError("Username missing for user ID: {UserId}", user.Id);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Username not found.", "Username not found.", 404));
|
||||
}
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(new { token = newJwtToken, refreshToken = newRefreshToken }, "User refresh token generated successfully.", 200));
|
||||
// Step 4: Fetch employee and generate new tokens
|
||||
var emp = await _employeeHelper.GetEmployeeByApplicationUserID(user.Id);
|
||||
|
||||
var newJwtToken = _refreshTokenService.GenerateJwtToken(user.UserName, emp.TenantId, _jwtSettings);
|
||||
var newRefreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, emp.TenantId.ToString(), _jwtSettings);
|
||||
|
||||
_logger.LogInfo("New access and refresh token issued for user: {UserId}", user.Id);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(
|
||||
new { token = newJwtToken, refreshToken = newRefreshToken },
|
||||
"User refresh token generated successfully.",
|
||||
200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("An unexpected error occurred during token refresh. : {Error}", ex.Message);
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error occurred.", ex.Message, 500));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("forgot-password")]
|
||||
public async Task<IActionResult> ForgotPassword([FromBody] ForgotPasswordDto forgotPasswordDto)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(forgotPasswordDto.Email))
|
||||
{
|
||||
_logger.LogWarning("ForgotPassword request received without email.");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Email is required.", "Email is required.", 400));
|
||||
}
|
||||
|
||||
var user = await _userManager.FindByEmailAsync(forgotPasswordDto.Email);
|
||||
if (user == null)
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("User not found.", "User not found.", 404));
|
||||
if (user == null || user.Email == null)
|
||||
{
|
||||
_logger.LogWarning("ForgotPassword requested for non-existent or null-email user: {Email}", forgotPasswordDto.Email);
|
||||
// Do not disclose whether the email exists (security best practice)
|
||||
return Ok(ApiResponse<object>.SuccessResponse(true, "Password reset link sent if the account exists.", 200));
|
||||
}
|
||||
|
||||
/* SEND USER REGISTRATION MAIL*/
|
||||
var token = await _userManager.GeneratePasswordResetTokenAsync(user);
|
||||
var resetLink = $"{_configuration["AppSettings:WebFrontendUrl"]}/reset-password?token={WebUtility.UrlEncode(token)}";
|
||||
try
|
||||
{
|
||||
// Generate token and build reset link
|
||||
var token = await _userManager.GeneratePasswordResetTokenAsync(user);
|
||||
var resetLink = $"{_configuration["AppSettings:WebFrontendUrl"]}/reset-password?token={WebUtility.UrlEncode(token)}";
|
||||
|
||||
if (user.Email == null) return NotFound(ApiResponse<object>.ErrorResponse("Email Not found", "Email Not found", 404));
|
||||
// Send reset email
|
||||
await _emailSender.SendResetPasswordEmail(user.Email, user.UserName ?? "User", resetLink);
|
||||
|
||||
await _emailSender.SendResetPasswordEmail(user.Email, "", resetLink);
|
||||
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(true, "Password reset link sent.", 200));
|
||||
_logger.LogInfo("Password reset link sent to user: {Email}", user.Email);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(true, "Password reset link sent if the account exists.", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Error while sending password reset email to: {Error}", ex.Message);
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Error sending password reset email.", ex.Message, 500));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("reset-password")]
|
||||
public async Task<IActionResult> ResetPassword([FromBody] ResetPasswordDto model)
|
||||
{
|
||||
var user = await _userManager.FindByEmailAsync(model.Email ?? string.Empty);
|
||||
if (user == null)
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid request.", "Invalid request.", 400));
|
||||
_logger.LogInfo("Password reset request received for email: {Email}", model.Email ?? string.Empty);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(model.Email) || string.IsNullOrWhiteSpace(model.Token) || string.IsNullOrWhiteSpace(model.NewPassword))
|
||||
{
|
||||
_logger.LogWarning("Reset password failed due to missing input fields for email: {Email}", model.Email ?? string.Empty);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("All fields are required.", "Invalid input.", 400));
|
||||
}
|
||||
|
||||
var user = await _userManager.FindByEmailAsync(model.Email);
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogWarning("Reset password failed - user not found for email: {Email}", model.Email);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid request.", "Invalid user.", 400));
|
||||
}
|
||||
|
||||
// var isTokenValid = await _userManager.VerifyUserTokenAsync(user,UserManager<ApplicationUser>.ResetPasswordTokenPurpose, model.ResetCode);
|
||||
var isTokenValid = await _userManager.VerifyUserTokenAsync(
|
||||
user,
|
||||
TokenOptions.DefaultProvider, // This is the token provider
|
||||
UserManager<ApplicationUser>.ResetPasswordTokenPurpose,
|
||||
WebUtility.UrlDecode(model.Token)
|
||||
);
|
||||
user,
|
||||
TokenOptions.DefaultProvider, // This is the token provider
|
||||
UserManager<ApplicationUser>.ResetPasswordTokenPurpose,
|
||||
WebUtility.UrlDecode(model.Token)
|
||||
);
|
||||
string token = "";
|
||||
|
||||
if (!isTokenValid)
|
||||
{
|
||||
_logger.LogWarning("Decoded token failed, retrying with raw token for email: {Email}", model.Email);
|
||||
|
||||
var isDecodedTokenValid = await _userManager.VerifyUserTokenAsync(
|
||||
user,
|
||||
TokenOptions.DefaultProvider, // This is the token provider
|
||||
TokenOptions.DefaultProvider,
|
||||
UserManager<ApplicationUser>.ResetPasswordTokenPurpose,
|
||||
model.Token
|
||||
model.Token
|
||||
);
|
||||
|
||||
if (!isDecodedTokenValid)
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid request.", "Invalid request.", 400));
|
||||
{
|
||||
_logger.LogWarning("Both decoded and raw token failed for email: {Email}", model.Email);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid request.", "Invalid or expired reset token.", 400));
|
||||
}
|
||||
|
||||
token = model.Token;
|
||||
}
|
||||
@ -263,26 +461,31 @@ namespace MarcoBMS.Services.Controllers
|
||||
token = WebUtility.UrlDecode(model.Token);
|
||||
}
|
||||
|
||||
|
||||
var result = await _userManager.ResetPasswordAsync(user, token, model.NewPassword ?? string.Empty);
|
||||
var result = await _userManager.ResetPasswordAsync(user, token, model.NewPassword);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
var errors = result.Errors.Select(e => e.Description).ToList();
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Failed to Change password", errors, 400));
|
||||
_logger.LogWarning("Reset password failed for user: {Email}. Errors: {Errors}", model.Email, string.Join(", ", errors));
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Failed to reset password.", errors, 400));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Employee emp = await _employeeHelper.GetEmployeeByApplicationUserID(user.Id);
|
||||
await _emailSender.SendResetPasswordSuccessEmail(user.Email ?? string.Empty, emp.FirstName + " " + emp.LastName);
|
||||
var emp = await _employeeHelper.GetEmployeeByApplicationUserID(user.Id);
|
||||
string fullName = $"{emp.FirstName} {emp.LastName}".Trim();
|
||||
|
||||
await _emailSender.SendResetPasswordSuccessEmail(user.Email!, fullName);
|
||||
|
||||
_logger.LogInfo("Reset password success email sent to user: {Email}", model.Email);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse(ex.Message, ex.Message, 400));
|
||||
_logger.LogError("Error while sending reset password success email to user: {Error}", ex.Message);
|
||||
// Continue, do not fail because of email issue
|
||||
}
|
||||
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(result.Succeeded, "Password reset successfully.", 200));
|
||||
_logger.LogInfo("Password reset successful for user: {Email}", model.Email);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(true, "Password reset successfully.", 200));
|
||||
}
|
||||
|
||||
[HttpPost("send-otp")]
|
||||
|
Loading…
x
Reference in New Issue
Block a user