Files
Finlytic/FinlyticBackend/Controllers/AuthController.cs
T

143 lines
4.7 KiB
C#

using System;
using System.Security.Claims;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using FinlyticBackend.Services;
using FinlyticCore.Models.Auth;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
namespace FinlyticBackend.Controllers;
/// <summary>
/// DTO representing the current user's profile (AOT-compliant).
/// </summary>
public record UserProfileResponseDto(
[property: JsonPropertyName("userId")] string UserId,
[property: JsonPropertyName("email")] string Email,
[property: JsonPropertyName("fullName")] string FullName,
[property: JsonPropertyName("role")] string Role
);
public record ChangeInitialPasswordDto(
[property: JsonPropertyName("userId")] Guid UserId,
[property: JsonPropertyName("newPassword")] string NewPassword
);
public class ForgotPasswordRequestDto
{
[JsonPropertyName("email")]
public string Email { get; set; } = string.Empty;
}
[ApiController]
[Route("api/v1")]
public class AuthController : ControllerBase
{
private readonly IUserService _userService;
public AuthController(IUserService userService)
{
_userService = userService;
}
/// <summary>
/// Authenticates a user and returns a JWT token.
/// </summary>
[HttpPost("auth/login")]
public async Task<IActionResult> Login([FromBody] LoginRequestDto request, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(request.Email) || string.IsNullOrWhiteSpace(request.Password))
{
return BadRequest(new { error = "Email and Password are required." });
}
var authResult = await _userService.AuthenticateAsync(request, cancellationToken);
if (authResult == null)
{
return Unauthorized(new { error = "Invalid credentials or user account is inactive." });
}
return Ok(authResult);
}
/// <summary>
/// Changes the initial password required by an admin reset or creation.
/// </summary>
[HttpPost("auth/change-initial-password")]
public async Task<IActionResult> ChangeInitialPassword([FromBody] ChangeInitialPasswordDto request, CancellationToken cancellationToken)
{
if (request.UserId == Guid.Empty || string.IsNullOrWhiteSpace(request.NewPassword))
{
return BadRequest(new { error = "UserId and NewPassword are required." });
}
bool changed = await _userService.ChangeInitialPasswordAsync(request.UserId, request.NewPassword, cancellationToken);
if (!changed)
{
return BadRequest(new { error = "Password change failed. User not found or password change not required." });
}
return Ok(new { message = "Password changed successfully. You may now login." });
}
/// <summary>
/// Updates the user's FCM device token.
/// </summary>
[Authorize]
[HttpPost("user/fcm-token")]
public async Task<IActionResult> UpdateFcmToken([FromBody] UpdateFcmTokenRequestDto request, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(request?.FcmToken))
{
return BadRequest(new { error = "FcmToken is required." });
}
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? User.FindFirst("sub")?.Value;
if (!Guid.TryParse(userIdClaim, out var userId))
{
return Unauthorized(new { error = "Invalid User Token claim." });
}
bool success = await _userService.RegisterOrUpdateFcmTokenAsync(userId, request.FcmToken, request.DeviceName ?? "Unknown Device", cancellationToken);
if (!success)
{
return BadRequest(new { error = "Failed to update FCM device token." });
}
return Ok(new { message = "FCM device token registered successfully." });
}
/// <summary>
/// Gets the current user's profile details.
/// </summary>
[Authorize]
[HttpGet("user/me")]
public IActionResult GetCurrentUserProfile()
{
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value
?? User.FindFirst("sub")?.Value
?? string.Empty;
var email = User.FindFirst(ClaimTypes.Email)?.Value
?? User.FindFirst("email")?.Value
?? string.Empty;
var name = User.FindFirst(ClaimTypes.Name)?.Value
?? User.FindFirst("name")?.Value
?? string.Empty;
var role = User.FindFirst(ClaimTypes.Role)?.Value
?? User.FindFirst("role")?.Value
?? "User";
return Ok(new UserProfileResponseDto(
UserId: userId,
Email: email,
FullName: name,
Role: role
));
}
}