added the logs
This commit is contained in:
parent
c5385c0b06
commit
7335ad23ce
@ -146,8 +146,20 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("login-mobile")]
|
[HttpPost("login-mobile")]
|
||||||
|
/// <summary>
|
||||||
|
/// Handles mobile user login, validates credentials, sends a test push notification,
|
||||||
|
/// and generates JWT, Refresh, and MPIN tokens upon successful authentication.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="loginDto">Data Transfer Object containing the user's login credentials and device token.</param>
|
||||||
|
/// <returns>An IActionResult containing the authentication tokens or an error response.</returns>
|
||||||
public async Task<IActionResult> LoginMobile([FromBody] LoginDto loginDto)
|
public async Task<IActionResult> LoginMobile([FromBody] LoginDto loginDto)
|
||||||
{
|
{
|
||||||
|
// Log the start of the login attempt for traceability.
|
||||||
|
_logger.LogInfo("Login attempt initiated for user: {Username}", loginDto.Username ?? "N/A");
|
||||||
|
|
||||||
|
// --- Push Notification Section ---
|
||||||
|
// This section attempts to send a test push notification to the user's device.
|
||||||
|
// It's designed to fail gracefully and handle invalid Firebase Cloud Messaging (FCM) tokens.
|
||||||
var message = new Message()
|
var message = new Message()
|
||||||
{
|
{
|
||||||
Token = loginDto.DeviceToken,
|
Token = loginDto.DeviceToken,
|
||||||
@ -157,95 +169,135 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
Body = "This is a test message"
|
Body = "This is a test message"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Attempt to send the message via Firebase.
|
||||||
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
|
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
|
||||||
_logger.LogInfo("Successfully sent message: {MessageId}", response);
|
_logger.LogInfo("Successfully sent test push notification. MessageId: {MessageId}", response);
|
||||||
}
|
}
|
||||||
catch (FirebaseMessagingException ex)
|
catch (FirebaseMessagingException ex)
|
||||||
{
|
{
|
||||||
_logger.LogError("Error sending push notification. : {Error}", ex.Message);
|
// Log the specific Firebase error.
|
||||||
|
_logger.LogError("Error sending push notification: {Error}", ex.Message);
|
||||||
|
|
||||||
// Check for the specific error codes that indicate an invalid token
|
// Check for specific error codes that indicate an invalid or unregistered token.
|
||||||
if (ex.MessagingErrorCode == MessagingErrorCode.Unregistered ||
|
if (ex.MessagingErrorCode == MessagingErrorCode.Unregistered ||
|
||||||
ex.MessagingErrorCode == MessagingErrorCode.InvalidArgument)
|
ex.MessagingErrorCode == MessagingErrorCode.InvalidArgument)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("FCM token is invalid and should be deleted: {Token}", loginDto.DeviceToken);
|
_logger.LogWarning("FCM token is invalid and should be deleted from the database: {Token}", loginDto.DeviceToken ?? string.Empty);
|
||||||
|
|
||||||
// Add your logic here to remove the invalid token from your database
|
// TODO: Implement the logic here to remove the invalid token from your database.
|
||||||
// await YourTokenService.DeleteTokenAsync(recordAttendanceDot.DeviceToken);
|
// Example: await YourTokenService.DeleteTokenAsync(loginDto.DeviceToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Validate input DTO
|
|
||||||
if (loginDto == null || string.IsNullOrWhiteSpace(loginDto.Username) || string.IsNullOrWhiteSpace(loginDto.Password))
|
try
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Login failed: User not found for input {Username}", loginDto.Username ?? string.Empty);
|
// --- Input Validation ---
|
||||||
return BadRequest(ApiResponse<object>.ErrorResponse("Username or password is missing.", "Invalid request", 400));
|
// Ensure that the request body and essential fields are not null or empty.
|
||||||
|
if (loginDto == null || string.IsNullOrWhiteSpace(loginDto.Username) || string.IsNullOrWhiteSpace(loginDto.Password))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Login failed due to missing username or password.");
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse("Username or password is missing.", "Invalid request", 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- User Retrieval ---
|
||||||
|
// Find the user in the database by their email or phone number.
|
||||||
|
_logger.LogInfo("Searching for user: {Username}", loginDto.Username);
|
||||||
|
var user = await _context.ApplicationUsers
|
||||||
|
.FirstOrDefaultAsync(u => u.Email == loginDto.Username || u.PhoneNumber == loginDto.Username);
|
||||||
|
|
||||||
|
// If no user is found, return an unauthorized response.
|
||||||
|
if (user == null || string.IsNullOrWhiteSpace(user.UserName))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Login failed: User not found for username {Username}", loginDto.Username);
|
||||||
|
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid username or password.", "Invalid username or password.", 401));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- User Status Checks ---
|
||||||
|
// Check if the user's account is marked as inactive.
|
||||||
|
if (!user.IsActive)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Login failed: User '{Username}' account is inactive.", user.UserName);
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse("User is inactive", "User is inactive", 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the user has confirmed their email address.
|
||||||
|
if (!user.EmailConfirmed)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Login failed: User '{Username}' email is not verified.", user.UserName);
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse("Your email is not verified. Please verify your email.", "Email not verified", 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Password Validation ---
|
||||||
|
// Use ASP.NET Identity's UserManager to securely check the password.
|
||||||
|
_logger.LogInfo("Validating password for user: {Username}", user.UserName);
|
||||||
|
var isPasswordValid = await _userManager.CheckPasswordAsync(user, loginDto.Password);
|
||||||
|
if (!isPasswordValid)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Login failed: Invalid password for user {Username}", user.UserName);
|
||||||
|
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid username or password.", "Invalid credentials", 401));
|
||||||
|
}
|
||||||
|
_logger.LogInfo("Password validation successful for user: {Username}", user.UserName);
|
||||||
|
|
||||||
|
// Check if the username property on the user object is populated.
|
||||||
|
if (string.IsNullOrWhiteSpace(user.UserName))
|
||||||
|
{
|
||||||
|
// This is an unlikely edge case, but good to handle.
|
||||||
|
_logger.LogError("Login failed: User object for ID {UserId} is missing a UserName.", user.Id);
|
||||||
|
return NotFound(ApiResponse<object>.ErrorResponse("UserName not found", "Username is missing", 404));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Employee and Tenant Context Retrieval ---
|
||||||
|
// Fetch associated employee details to get tenant context for token generation.
|
||||||
|
_logger.LogInfo("Fetching employee details for user ID: {UserId}", user.Id);
|
||||||
|
var emp = await _employeeHelper.GetEmployeeByApplicationUserID(user.Id);
|
||||||
|
if (emp == null)
|
||||||
|
{
|
||||||
|
_logger.LogError("Login failed: Could not find associated employee record for user ID {UserId}", user.Id);
|
||||||
|
return NotFound(ApiResponse<object>.ErrorResponse("Employee not found", "Employee details missing", 404));
|
||||||
|
}
|
||||||
|
_logger.LogInfo("Successfully found employee details for tenant ID: {TenantId}", emp.TenantId);
|
||||||
|
|
||||||
|
// --- Token Generation ---
|
||||||
|
// Generate the primary JWT access token.
|
||||||
|
_logger.LogInfo("Generating JWT for user: {Username}", user.UserName);
|
||||||
|
var token = _refreshTokenService.GenerateJwtToken(user.UserName, emp.TenantId, _jwtSettings);
|
||||||
|
|
||||||
|
// Generate a new refresh token and store it in the database.
|
||||||
|
_logger.LogInfo("Generating and storing Refresh Token for user: {Username}", user.UserName);
|
||||||
|
var refreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, emp.TenantId.ToString(), _jwtSettings);
|
||||||
|
|
||||||
|
// Fetch the user's MPIN token if it exists.
|
||||||
|
_logger.LogInfo("Fetching MPIN token for user: {Username}", user.UserName);
|
||||||
|
var mpinToken = await _context.MPINDetails.FirstOrDefaultAsync(p => p.UserId == Guid.Parse(user.Id) && p.TenantId == emp.TenantId);
|
||||||
|
|
||||||
|
// --- Response Assembly ---
|
||||||
|
// Combine all tokens into a single response object.
|
||||||
|
var responseData = new
|
||||||
|
{
|
||||||
|
token,
|
||||||
|
refreshToken,
|
||||||
|
mpinToken = mpinToken?.MPINToken // Safely access the MPIN token, will be null if not found.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return a successful response with the generated tokens.
|
||||||
|
_logger.LogInfo("User {Username} logged in successfully.", user.UserName);
|
||||||
|
return Ok(ApiResponse<object>.SuccessResponse(responseData, "User logged in successfully.", 200));
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
// Find user by email or phone number
|
|
||||||
var user = await _context.ApplicationUsers
|
|
||||||
.FirstOrDefaultAsync(u => u.Email == loginDto.Username || u.PhoneNumber == loginDto.Username);
|
|
||||||
|
|
||||||
// If user not found, return unauthorized
|
|
||||||
if (user == null)
|
|
||||||
{
|
{
|
||||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid username or password.", "Invalid username or password.", 401));
|
// --- Global Exception Handling ---
|
||||||
|
// Catch any unexpected exceptions during the login process.
|
||||||
|
_logger.LogError("An unexpected error occurred during the LoginMobile process for user: {Username} : {Error}", loginDto?.Username ?? "N/A", ex.Message);
|
||||||
|
|
||||||
|
// Return a generic 500 Internal Server Error to avoid leaking implementation details.
|
||||||
|
return StatusCode(500, ApiResponse<object>.ErrorResponse("An internal server error occurred.", "Server Error", 500));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user is inactive
|
|
||||||
if (!user.IsActive)
|
|
||||||
{
|
|
||||||
return BadRequest(ApiResponse<object>.ErrorResponse("User is inactive", "User is inactive", 400));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user email is not confirmed
|
|
||||||
if (!user.EmailConfirmed)
|
|
||||||
{
|
|
||||||
return BadRequest(ApiResponse<object>.ErrorResponse("Your email is not verified. Please verify your email.", "Email not verified", 400));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate password using ASP.NET Identity
|
|
||||||
var isPasswordValid = await _userManager.CheckPasswordAsync(user, loginDto.Password);
|
|
||||||
if (!isPasswordValid)
|
|
||||||
{
|
|
||||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid username or password.", "Invalid credentials", 401));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if username is missing
|
|
||||||
if (string.IsNullOrWhiteSpace(user.UserName))
|
|
||||||
{
|
|
||||||
return NotFound(ApiResponse<object>.ErrorResponse("UserName not found", "Username is missing", 404));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get employee information for tenant context
|
|
||||||
var emp = await _employeeHelper.GetEmployeeByApplicationUserID(user.Id);
|
|
||||||
if (emp == null)
|
|
||||||
{
|
|
||||||
return NotFound(ApiResponse<object>.ErrorResponse("Employee not found", "Employee details missing", 404));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate JWT token
|
|
||||||
var token = _refreshTokenService.GenerateJwtToken(user.UserName, emp.TenantId, _jwtSettings);
|
|
||||||
|
|
||||||
// Generate Refresh Token and store in DB
|
|
||||||
var refreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, emp.TenantId.ToString(), _jwtSettings);
|
|
||||||
|
|
||||||
// Fetch MPIN Token
|
|
||||||
var mpinToken = await _context.MPINDetails.FirstOrDefaultAsync(p => p.UserId == Guid.Parse(user.Id) && p.TenantId == emp.TenantId);
|
|
||||||
|
|
||||||
// Combine all tokens in response
|
|
||||||
var responseData = new
|
|
||||||
{
|
|
||||||
token,
|
|
||||||
refreshToken,
|
|
||||||
mpinToken = mpinToken?.MPINToken
|
|
||||||
};
|
|
||||||
|
|
||||||
// Return success response
|
|
||||||
return Ok(ApiResponse<object>.SuccessResponse(responseData, "User logged in successfully.", 200));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpPost("login-mpin")]
|
[HttpPost("login-mpin")]
|
||||||
public async Task<IActionResult> VerifyMPIN([FromBody] VerifyMPINDto verifyMPIN)
|
public async Task<IActionResult> VerifyMPIN([FromBody] VerifyMPINDto verifyMPIN)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user